(1) Couldn't you push/pop the registers to the stack? sp has to be preserved at exit of the ISR, but that doesn't mean it can never be modified. As long as all the pushes are matched by pops sp will come out the same. That way you won't need any temporary registers (the only register that'll be modified is sp).
(2) If you're using C code you don't need to explicitly save r8-r14, the compiler will do that for you if it uses them.
(3) Watch out for r15 (lr). You'll need to save that inside the ISR.
When you modify prefix.spin2 you must put your code at the end of the file, just before the "orgh $400". Then you must assemble it by doing "p2asm -o prefix.spin2". If you make any changes to prefix.spin2 at the beginning or middle of the file that change the addresses of the registers or routines that are in cog memory, not only must you assemble prefix.spin2, but you must also rebuild the library.
(1) Couldn't you push/pop the registers to the stack? sp has to be preserved at exit of the ISR, but that doesn't mean it can never be modified. As long as all the pushes are matched by pops sp will come out the same. That way you won't need any temporary registers (the only register that'll be modified is sp).
(2) If you're using C code you don't need to explicitly save r8-r14, the compiler will do that for you if it uses them.
(3) Watch out for r15 (lr). You'll need to save that inside the ISR.
1) stack sounds much better. I'll try that. Can I use the built-in push/pop instructions? Hard to find much info on that in the manual, but one spot implies that it's only 8 words large... That doesn't help us. So I should be able to do
wrlong sp, r0
I'll try that soon.
2) good point. But can I trust that it will restore the registers before the reti instruction that must be manually inserted? If the compiler automatically changed the ret to reti, then I'd trust it for sure.
When you modify prefix.spin2 you must put your code at the end of the file, just before the "orgh $400". Then you must assemble it by doing "p2asm -o prefix.spin2". If you make any changes to prefix.spin2 at the beginning or middle of the file that change the addresses of the registers or routines that are in cog memory, not only must you assemble prefix.spin2, but you must also rebuild the library.
I will try rebuilding the library even after prime changes at the end. I stated the endeavor by inserting my blocks right before the orgh $400 and was getting unexpected behavior.
It just occurred to me though... Does it matter if it's above or below the comment on to of orgh $400? I was putting it above the comment, not below.
Umm, my advise is don't try to. ISRs in the propeller, like "objects" in the Obex, should be integral to the program running in that cog. Each use is custom to suit the job. Mostly that means everything is global within that cog.
Conventions for utilisation of hubRAM might be a good idea though.
I'm not trying (at this moment... I will later) to write a shareable isr, I'm trying to make it possible to write an isr in C with p2gcc, which goes through a compiler never intended to support interrupts. Right now, only the simplest case is possible with my only working isr consisting of a drvnot and reti instruction. I know isr should stay simple... But I'd like a little more complexity than that. And even if I use nothing but inline assembly, I'll likely need to access some global variables in that assembly. At that point, propgcc is liable to clobber the registers. So, to do anything useful in an isr, I need to be able to save off the registers. Whether or not that isr will be useful to anyone else or any other application is another story.
I think you have context switching confused with Interrupt service routine.
When the compile has to build an ISR it will know what registers it will use and save them prior to modification. When it has to return it will restore those registers.
When the compile has to build an ISR it will know what registers it will use and save them prior to modification. When it has to return it will restore those registers.
"Should" and "will" are two different things. You meant "should" . Problem is, our compiler doesn't know what an ISR is and therefore won't do anything special. Until work on PropGCC for P2 starts, the best we have is p2gcc (which has been a life saver since, if it hasn't become glaringly apparent yet, my Spin is very rusty).
Like Eric said, the compiler will generate code to preserve r8-r14. It doesn't know about interrupts, so an ISR needs to save r0-r7 on entry, and restore them when exiting.
I've been testing p2gcc, and some erros occur when I try to compile my (infamous) prime number calculator program. This program used to run on the P1, before being modified to the P2. Here is the source:
/*
* Programa de cálculo de números primos para a placa de desenvolvimento
* "Prop II"
*
* Este programa foi criado para verificar o desempenho da placa "Prop II",
* mas pode ser utilizado com outras placas de desenvolvimento baseadas no
* Propeller P8X32A. O programa calcula números primos dentro de uma dada
* gama, cujos valores são introduzidos pelo utilizador.
*
* Nota:
* Para o máximo desempenho, o programa deve ser compilado utilizando o modelo
* de memória "LMM Main RAM". Deverá seleccionar a opção "Math Lib" no
* separador "Linker".
*
* Autor: Samuel Lourenço
* Data: 27 de Agosto de 2016
*/
// Includes
#include <math.h>
#include <propeller.h>
#include <stdio.h>
#include <stdlib.h>
// Protótipos
void init(void);
_Bool isprime(unsigned long long);
void isprime_cog(void* param);
// Variáveis globais
static char lockid;
unsigned int stacks[7][44];
static volatile _Bool flags[7] = {0, 0, 0, 0, 0, 0, 0}, results[7];
static volatile unsigned long long numbers[7] = {0, 0, 0, 0, 0, 0, 0};
/* Tanto o array "flags" como o array "numbers" são indicadores de estado, e por
* isso devem ser inicializados a zero. O array "flags" indica a disponibilidade
* de cada 'cog' (zero para "inactivo" e um para "activo"), ao passo que o
* array "numbers" é indicativo das tarefas, uma vez que armazena os números
* processar (um valor a zero significa que o 'cog' correspondente está livre).
* Estes arrays são implicitamente limpos depois da sessão de cálculo. O array
* "results" não é indicador de estado, pelo que não precisa de ser
* inicializado a zero. */
void main(void)
{
long long count, min, max, in, out;
char input[256];
init();
while (1)
{
count = 0;
min = 0;
max = 0;
/* É importante limpar as variáveis anteriores antes de cada ciclo de
* cálculo. */
while (min <= 0)
{
printf("Insira o valor mínimo: ");
fgets(input, 256, stdin); // Lê a string introduzida
min = atoll (input); // Converte para 'long long integer'
if (min <= 0) // Se o valor introduzido for menor ou igual a zero, ou não for um número
printf("O valor mínimo deve ser um número maior do que zero!\n");
}
while (max <= 0)
{
printf("Insira o valor máximo: ");
fgets(input, 256, stdin); // Lê a string introduzida
max = atoll (input); // Converte para 'long long integer'
if (max <= 0) // Se o valor introduzido for menor ou igual a zero, ou não for um número
printf("O valor máximo deve ser um número maior do que zero!\n");
}
if (min <= max) // Se o valor mínimo for menor ou igual ao valor máximo
{
printf("\nA calcular números primos...\n\n");
out = in = min;
/* A variável "in" guarda o valor a introduzir, enquanto que a
* variável "out" serve para fazer uma gestão dos números a
* verificar depois do cálculo. Estas variáveis vão sendo
* incrementadas por cada número que é introduzido ou verificado no
* array "numbers". Note que o valor em "out" não corresponde
* necessariamente ao valor que está a ser extraído do array. */
while (out <= max)
{
char i;
for (i = 0; i < 7; i++) // Este ciclo "varre" os valores dos arrays "flags", "numbers" e "results"
{
while (lockset(lockid) == -1) // Espera pelo 'lock' dos recursos
;
if (!flags[i] && numbers[i] != 0) // Se o 'cog' estiver marcado como estando "inactivo" e o número não for zero
{
if (results[i]) // Se o número for primo
{
count++; // Incrementa o valor da variável "count", que serve para a contagem dos números primos
printf("%lld:\t%llu\n", count, numbers[i]); // Imprime a contagem e o número
}
numbers[i] = 0; // Uma vez verificado se é primo ou não, limpa o número na posição 'i'
out++; // Incrementa o valor da variável "out"
}
if (in <= max && numbers[i] == 0) // Se houver novos números a introduzir e se houver 'cogs' livres
{
numbers[i] = (unsigned long long)in; // Introduz o número da variável "in" no array "numbers", posição 'i'
flags[i] = 1; // Marca o 'cog' correspondente ao índex como "activo", efectivamente dando sinal ao mesmo para arrancar
in++; // Incrementa o valor de "in"
}
lockclr(lockid); // Retira o 'lock' dos recursos
}
}
printf("\n%lld número(s) primo(s) encontrados.\n\n", count); // Apresenta a contagem final de números primos
}
else // Se o valor mínimo for maior do que o valor máximo
printf("O valor mínimo não pode ser maior do que o valor máximo!\n");
}
}
void init (void) // Rotina de inicialização do micro-controlador
{
lockid = (char)locknew(); // Cria um 'lock'
lockclr(lockid); // Limpa o estado do 'lock' criado, garantindo que este fica a zero
char i;
for (i = 0; i < 7; i++) // Os 'cogs' 1 a 7 são inicializados aqui, sempre com a mesma rotina
{
cogstart(isprime_cog, NULL, stacks[i], sizeof(stacks[i])); // Inicia cada 'cog' com a rotina "isprime_cog" e atribui-lhe o seu 'stack'
}
}
_Bool isprime(unsigned long long number) // Função para cálculo de números primos
{
/* Esta pode ser considerada a função central do programa. Para ver se um
* dado número é primo ou não, primeiro verifica-se se o mesmo é um, dois,
* três, ou se é divisível por dois ou três. Considera-se que se o número
* for igual a um, ou divisível por dois ou três, não é primo. Sendo dois
* ou três, é primo. Não se satisfazendo qualquer das condições
* anteriores, verifica-se se o número é divisível por todos os números
* ímpares não múltiplos de três, a partir de cinco até (pelo menos) ao
* inteiro da raiz quadrada do mesmo (inclusive). Repare que não é
* necessário ensaiar todas as divisões até o quociente ser igual ao
* próprio número. Deste modo, o algoritmo fica muito mais rápido. */
_Bool retval;
unsigned long sqrt_num;
if (number == 2 || number == 3) // Se o número for dois ou três
retval = 1; // então é primo
else if (number < 2 || number % 2 == 0 || number % 3 == 0) // Se for zero ou um, ou divisível por dois ou por três (não sendo dois ou três)
retval = 0; // não é primo
else
{
retval = 1; // Assume-se que o número é primo, até haver uma divisão inteira
sqrt_num = (unsigned long)sqrt(number); // Calcula previamente a raíz quadrada do número
unsigned long n;
for (n = 5; n <= sqrt_num; n += 6) // Ciclo onde são testadas as divisões por números ímpares que não sejam múltiplos de três
{
if (number % n == 0 || number % (n + 2) == 0) // Se a divisão por 'n' ou por 'n + 2' for inteira
{
retval = 0; // o número não é primo
break; // Sai do ciclo
}
}
}
return retval; // Retorna o valor um se o número dado for primo, e zero se não o for
}
void isprime_cog(void *param) // Rotina dedicada a cada 'cog'
{
_Bool flag, result;
unsigned long long number;
char i = cogid() - 1; // Vê de antemão qual é o 'cog' que está a executar esta rotina, e associa o valor obtido a um índex
while(1)
{
while (lockset(lockid) == -1) // Espera pelo 'lock' dos recursos
;
flag = flags[i]; // Lê as variáveis "flag"
number = numbers[i]; // e "number" dirigida ao 'cog' que está a executar este procedimento
lockclr(lockid); // Retira o 'lock'
/* Os recursos devem estar disponíveis para os outros 'cogs' enquanto
* se verifica se o número é primo ou não. Se assim não fosse, não seria
* possível ter vários 'cogs' a fazer esta verificação em simultâneo. */
if (flag) // Se o 'cog' estiver marcado como activo, então inicia o cálculo
{
result = isprime(number); // Calcula se o número dado é primo ou não
while (lockset(lockid) == -1) // Espera pelo 'lock' dos recursos
;
results[i] = result; // Devolve o resultado (se é primo ou não)
flags[i] = 0; // Marca o 'cog' como "inactivo"
lockclr(lockid); // Retira o 'lock'
}
}
}
However, when I try to compile with p2gcc (p2gcc -v -t -s primos.c -o primos), it returns the following:
propeller-elf-gcc -mcog -Os -m32bit-doubles -S primos.c
s2pasm -p/nix/store/nr1kgjlgv3b4wfni9rx2lm5590z6fbpc-p2gcc-2019-01-13r0/lib/prefix.spin2 primos
p2asm -c -o primos.spin2
label lockclr is already defined
label lockset is already defined
label lockclr is already defined
label lockset is already defined
label lockclr is already defined
166: ERROR: Opcode type 39 is not supported
lockclr r7
329: ERROR: Opcode type 39 is not supported
lockset r7 wc
441: ERROR: Opcode type 39 is not supported
lockclr r7
621: ERROR: Opcode type 39 is not supported
lockset r7 wc
635: ERROR: Opcode type 39 is not supported
lockclr r7
642: ERROR: Opcode type 39 is not supported
lockset r7 wc
652: ERROR: Opcode type 39 is not supported
lockclr r7
p2link /nix/store/nr1kgjlgv3b4wfni9rx2lm5590z6fbpc-p2gcc-2019-01-13r0/lib/prefix.o -v primos.o -o primos /nix/store/nr1kgjlgv3b4wfni9rx2lm5590z6fbpc-p2gcc-2019-01-13r0/lib/stdio.a /nix/store/nr1kgjlgv3b4wfni9rx2lm5590z6fbpc-p2gcc-2019-01-13r0/lib/stdlib.a /nix/store/nr1kgjlgv3b4wfni9rx2lm5590z6fbpc-p2gcc-2019-01-13r0/lib/string.a
Found offset of 12 for symbol ___files of type 04 at location 1774
_cogstart is unresolved
_fgets is unresolved
_atoll is unresolved
_fgets is unresolved
_atoll is unresolved
___umoddi3 is unresolved
___floatundisf is unresolved
_sqrt is unresolved
___fixunssfsi is unresolved
___umoddi3 is unresolved
___umoddi3 is unresolved
11 symbol(s) are unresolved
I understand that some functions, like sqrt() and atoll() are not implemented yet. However, are then any substitutions for cogstart(), lockset() and the like? I can live without the prompt asking to insert the search limits, so I can dismiss atoll() if it needs to be so. I think sqrt() can be implemented as an assembly inline, but I don't know how to do it.
Since lockset/lockclr is not defined in the P2 the assembler assumes it is a label. When it encounters lockset/lockclr again it prints an error that it is already defined.
The message "Opcode type 39 is not supported" is not very informative. What I should print is "r7 is an invalid instruction".
I believe cogstart() is supported, because it produces a coginit, which is translated from the P1 version to P2. I could provide translations for lockset and lockclr, but I haven't at this time.
The sqrt() function is a floating point function. I have worked on supporting floating point, but I haven't posted that yet. Here is a square root function that works on integers.
I haven't added fgets() to the library yet. I do have it coded up, but I haven't posted it yet. Since you are reading from stdin, you can use gets() instead.
Here are the implementations of lockset/lockclr for P2 in spin2cpp. Note that this is Spin code, not C code, so you'll have to translate! It should be pretty straightforward though:
pri lockclr(id) | mask, rval
mask := -1
asm
lockrel id wc
muxc rval,mask
endasm
return rval
pri lockset(id) | mask, rval
mask := -1
asm
locktry id wc
muxnc rval,mask ' NOTE: C bit is opposite in P2
endasm
return rval
Since lockset/lockclr is not defined in the P2 the assembler assumes it is a label. When it encounters lockset/lockclr again it prints an error that it is already defined.
The message "Opcode type 39 is not supported" is not very informative. What I should print is "r7 is an invalid instruction".
I believe cogstart() is supported, because it produces a coginit, which is translated from the P1 version to P2. I could provide translations for lockset and lockclr, but I haven't at this time.
The sqrt() function is a floating point function. I have worked on supporting floating point, but I haven't posted that yet. Here is a square root function that works on integers.
I haven't added fgets() to the library yet. I do have it coded up, but I haven't posted it yet. Since you are reading from stdin, you can use gets() instead.
Thanks Dave! Managed to reduce the unresolved symbols to 4. Besides replaceing fgets() with the unsafe gets(), I've replaced atoll() and implemented isqrt() as you described. That was precious!
Here are the implementations of lockset/lockclr for P2 in spin2cpp. Note that this is Spin code, not C code, so you'll have to translate! It should be pretty straightforward though:
pri lockclr(id) | mask, rval
mask := -1
asm
lockrel id wc
muxc rval,mask
endasm
return rval
pri lockset(id) | mask, rval
mask := -1
asm
locktry id wc
muxnc rval,mask ' NOTE: C bit is opposite in P2
endasm
return rval
Hi ersmit. Unfortunately, I don't have any knowledge of Spin, and hardly I know how to call assembly functions from C. Nevertheless, I'll try my best to convert this.
Hi Pilot. It is not a GUI, at least just yet. It is p2gcc that you can install via nix, or compile directly (I recommend the first option). If your Linux distro is running on a VM, make sure you have at least 4GB of RAM before fetching p2gcc via nix.
loadp2 is part of the p2gcc suite of tools. The source code is located at https://github.com/davehein/p2gcc . You can also get the source and Windows executables from a zipfile posted in the first post of this thread. This is not as up to date as GitHub, but the latest zipfile is fairly recent.
My question is really how do you start it. I am confused with all the files in github. Where does one start first?
Thanks.
I do not know how to string all of those files together.
However, when I implemented the functions, it returned:
propeller-elf-gcc -mcog -Os -m32bit-doubles -S primos.c
primos.c:50:31: error: expected declaration specifiers or '...' before '(' token
primos.c:56:31: error: expected declaration specifiers or '...' before '(' token
I went to investigate, and found out that there was a conflict with the function names, that are defined somewhere. To solve this issue, I had to rename the lockset() and lockclr() functions. I've changed the code to this:
/*
* Programa de cálculo de números primos para a placa de desenvolvimento
* "Prop II"
*
* Este programa foi criado para verificar o desempenho da placa "Prop II",
* mas pode ser utilizado com outras placas de desenvolvimento baseadas no
* Propeller P8X32A. O programa calcula números primos dentro de uma dada
* gama, cujos valores são introduzidos pelo utilizador.
*
* Nota:
* Para o máximo desempenho, o programa deve ser compilado utilizando o modelo
* de memória "LMM Main RAM". Deverá seleccionar a opção "Math Lib" no
* separador "Linker".
*
* Autor: Samuel Lourenço
* Data: 27 de Agosto de 2016
*/
// Includes
#include <math.h>
#include <propeller.h>
#include <stdio.h>
#include <stdlib.h>
// Protótipos
void init(void);
_Bool isprime(unsigned long long);
void isprime_cog(void* param);
// Variáveis globais
static char lockid;
unsigned int stacks[7][44];
static volatile _Bool flags[7] = {0, 0, 0, 0, 0, 0, 0}, results[7];
static volatile unsigned long long numbers[7] = {0, 0, 0, 0, 0, 0, 0};
/* Tanto o array "flags" como o array "numbers" são indicadores de estado, e por
* isso devem ser inicializados a zero. O array "flags" indica a disponibilidade
* de cada 'cog' (zero para "inactivo" e um para "activo"), ao passo que o
* array "numbers" é indicativo das tarefas, uma vez que armazena os números
* processar (um valor a zero significa que o 'cog' correspondente está livre).
* Estes arrays são implicitamente limpos depois da sessão de cálculo. O array
* "results" não é indicador de estado, pelo que não precisa de ser
* inicializado a zero. */
int __attribute__((noinline)) isqrt(int x)
{
__asm__("qsqrt r0, #0");
__asm__("getqx r0");
}
int __attribute__((noinline)) lockclr2(int id)
{
__asm__("lockrel r0 wc");
__asm__("muxc r0,##-1");
}
int __attribute__((noinline)) lockset2(int id)
{
__asm__("locktry r0 wc");
__asm__("muxnc r0,##-1");
}
void main(void)
{
long long count, min, max, in, out;
char input[256];
init();
while (1)
{
count = 0;
min = 0;
max = 0;
/* É importante limpar as variáveis anteriores antes de cada ciclo de
* cálculo. */
while (min <= 0)
{
printf("Insira o valor mínimo: ");
gets(input); // Lê a string introduzida
min = atol (input); // Converte para 'long long integer'
if (min <= 0) // Se o valor introduzido for menor ou igual a zero, ou não for um número
printf("O valor mínimo deve ser um número maior do que zero!\n");
}
while (max <= 0)
{
printf("Insira o valor máximo: ");
gets(input); // Lê a string introduzida
max = atol (input); // Converte para 'long long integer'
if (max <= 0) // Se o valor introduzido for menor ou igual a zero, ou não for um número
printf("O valor máximo deve ser um número maior do que zero!\n");
}
if (min <= max) // Se o valor mínimo for menor ou igual ao valor máximo
{
printf("\nA calcular números primos...\n\n");
out = in = min;
/* A variável "in" guarda o valor a introduzir, enquanto que a
* variável "out" serve para fazer uma gestão dos números a
* verificar depois do cálculo. Estas variáveis vão sendo
* incrementadas por cada número que é introduzido ou verificado no
* array "numbers". Note que o valor em "out" não corresponde
* necessariamente ao valor que está a ser extraído do array. */
while (out <= max)
{
char i;
for (i = 0; i < 7; i++) // Este ciclo "varre" os valores dos arrays "flags", "numbers" e "results"
{
while (lockset2(lockid) == -1) // Espera pelo 'lock' dos recursos
;
if (!flags[i] && numbers[i] != 0) // Se o 'cog' estiver marcado como estando "inactivo" e o número não for zero
{
if (results[i]) // Se o número for primo
{
count++; // Incrementa o valor da variável "count", que serve para a contagem dos números primos
printf("%lld:\t%llu\n", count, numbers[i]); // Imprime a contagem e o número
}
numbers[i] = 0; // Uma vez verificado se é primo ou não, limpa o número na posição 'i'
out++; // Incrementa o valor da variável "out"
}
if (in <= max && numbers[i] == 0) // Se houver novos números a introduzir e se houver 'cogs' livres
{
numbers[i] = (unsigned long long)in; // Introduz o número da variável "in" no array "numbers", posição 'i'
flags[i] = 1; // Marca o 'cog' correspondente ao índex como "activo", efectivamente dando sinal ao mesmo para arrancar
in++; // Incrementa o valor de "in"
}
lockclr2(lockid); // Retira o 'lock' dos recursos
}
}
printf("\n%lld número(s) primo(s) encontrados.\n\n", count); // Apresenta a contagem final de números primos
}
else // Se o valor mínimo for maior do que o valor máximo
printf("O valor mínimo não pode ser maior do que o valor máximo!\n");
}
}
void init (void) // Rotina de inicialização do micro-controlador
{
lockid = (char)locknew(); // Cria um 'lock'
lockclr2(lockid); // Limpa o estado do 'lock' criado, garantindo que este fica a zero
char i;
for (i = 0; i < 7; i++) // Os 'cogs' 1 a 7 são inicializados aqui, sempre com a mesma rotina
{
cogstart(isprime_cog, NULL, stacks[i], sizeof(stacks[i])); // Inicia cada 'cog' com a rotina "isprime_cog" e atribui-lhe o seu 'stack'
}
}
_Bool isprime(unsigned long long number) // Função para cálculo de números primos
{
/* Esta pode ser considerada a função central do programa. Para ver se um
* dado número é primo ou não, primeiro verifica-se se o mesmo é um, dois,
* três, ou se é divisível por dois ou três. Considera-se que se o número
* for igual a um, ou divisível por dois ou três, não é primo. Sendo dois
* ou três, é primo. Não se satisfazendo qualquer das condições
* anteriores, verifica-se se o número é divisível por todos os números
* ímpares não múltiplos de três, a partir de cinco até (pelo menos) ao
* inteiro da raiz quadrada do mesmo (inclusive). Repare que não é
* necessário ensaiar todas as divisões até o quociente ser igual ao
* próprio número. Deste modo, o algoritmo fica muito mais rápido. */
_Bool retval;
unsigned long sqrt_num;
if (number == 2 || number == 3) // Se o número for dois ou três
retval = 1; // então é primo
else if (number < 2 || number % 2 == 0 || number % 3 == 0) // Se for zero ou um, ou divisível por dois ou por três (não sendo dois ou três)
retval = 0; // não é primo
else
{
retval = 1; // Assume-se que o número é primo, até haver uma divisão inteira
sqrt_num = (unsigned long)isqrt(number); // Calcula previamente a raíz quadrada do número
unsigned long n;
for (n = 5; n <= sqrt_num; n += 6) // Ciclo onde são testadas as divisões por números ímpares que não sejam múltiplos de três
{
if (number % n == 0 || number % (n + 2) == 0) // Se a divisão por 'n' ou por 'n + 2' for inteira
{
retval = 0; // o número não é primo
break; // Sai do ciclo
}
}
}
return retval; // Retorna o valor um se o número dado for primo, e zero se não o for
}
void isprime_cog(void *param) // Rotina dedicada a cada 'cog'
{
_Bool flag, result;
unsigned long long number;
char i = cogid() - 1; // Vê de antemão qual é o 'cog' que está a executar esta rotina, e associa o valor obtido a um índex
while(1)
{
while (lockset2(lockid) == -1) // Espera pelo 'lock' dos recursos
;
flag = flags[i]; // Lê as variáveis "flag"
number = numbers[i]; // e "number" dirigida ao 'cog' que está a executar este procedimento
lockclr2(lockid); // Retira o 'lock'
/* Os recursos devem estar disponíveis para os outros 'cogs' enquanto
* se verifica se o número é primo ou não. Se assim não fosse, não seria
* possível ter vários 'cogs' a fazer esta verificação em simultâneo. */
if (flag) // Se o 'cog' estiver marcado como activo, então inicia o cálculo
{
result = isprime(number); // Calcula se o número dado é primo ou não
while (lockset2(lockid) == -1) // Espera pelo 'lock' dos recursos
;
results[i] = result; // Devolve o resultado (se é primo ou não)
flags[i] = 0; // Marca o 'cog' como "inactivo"
lockclr2(lockid); // Retira o 'lock'
}
}
}
However, the compiler still complains about unresolved symbols, namely pertaining the cogstart() function. It returns the following:
propeller-elf-gcc -mcog -Os -m32bit-doubles -S primos.c
s2pasm -p/nix/store/nr1kgjlgv3b4wfni9rx2lm5590z6fbpc-p2gcc-2019-01-13r0/lib/prefix.spin2 primos
p2asm -c -o primos.spin2
p2link /nix/store/nr1kgjlgv3b4wfni9rx2lm5590z6fbpc-p2gcc-2019-01-13r0/lib/prefix.o -v primos.o -o primos /nix/store/nr1kgjlgv3b4wfni9rx2lm5590z6fbpc-p2gcc-2019-01-13r0/lib/stdio.a /nix/store/nr1kgjlgv3b4wfni9rx2lm5590z6fbpc-p2gcc-2019-01-13r0/lib/stdlib.a /nix/store/nr1kgjlgv3b4wfni9rx2lm5590z6fbpc-p2gcc-2019-01-13r0/lib/string.a
Found offset of 12 for symbol ___files of type 04 at location 173c
_cogstart is unresolved
___umoddi3 is unresolved
___umoddi3 is unresolved
___umoddi3 is unresolved
4 symbol(s) are unresolved
This seems to be a linker issue. Is there any way we can implement cogstart()? This is getting close, BTW. Thanks!
When I commented on cogstart in an earlier post I had forgotten about the stack. Currently the stack location is hardcoded in lib/prefix.spin2. The way it should work is that the stack address is passed in one of the pointers, either ptra or ptrb. I don't recall which one is available for passing an address. This is on my TODO list, but I haven't gotten around to it yet. I'll look into passing the stack address through the pointer, which will allow cogstart to work.
I have run across ___umoddi3 before when I was trying to port some C code from the internet. Here is some code you can use:
I haven't tested this code, but this is my best guess at how the ___umoddi3 routine works. I am using qdiv to get the remainder. qdiv does a signed division, so this code will fail for very large unsigned number. However, it should work for typical use. This routine will need to be fully implemented in the future to handle all cases.
I got cogstart() working. I copied cogstart.c and cogthread.c from the PropGCC library, and then create a cognew1() function, which is in cognew.spin2. I had to modify prefix.spin2 to check PTRA for a stack address. If PTRA is zero it starts up as before setting the stack address to a predefined value, and calls the main() routine. If PTRA is nonzero it will set the stack pointer to the value in PTRA, and then pops off the starting function address and a parameter that is passed to the function.
The new files are in the attached zipfile along with cogtest.c and a runit script file. The test program blinks P56 once per second. You will need to copy prefix.spin2 to the lib directory, and then run "p2asm -o prefix.spin2" in the lib directory to assemble it. You may want to save the current prefix.spin2 in case you want to revert back to it. Run the runit script to build the test program and to run it.
I got cogstart() working. I copied cogstart.c and cogthread.c from the PropGCC library, and then create a cognew1() function, which is in cognew.spin2. I had to modify prefix.spin2 to check PTRA for a stack address. If PTRA is zero it starts up as before setting the stack address to a predefined value, and calls the main() routine. If PTRA is nonzero it will set the stack pointer to the value in PTRA, and then pops off the starting function address and a parameter that is passed to the function.
The new files are in the attached zipfile along with cogtest.c and a runit script file. The test program blinks P56 once per second. You will need to copy prefix.spin2 to the lib directory, and then run "p2asm -o prefix.spin2" in the lib directory to assemble it. You may want to save the current prefix.spin2 in case you want to revert back to it. Run the runit script to build the test program and to run it.
Well done! Looking forward to playing with that. Thank you!
I've had some complications. I had to compile prefix.spin2 out of the destination directory, because "p2asm" does not run under sudo (it is a nix installation). But now that it has the prefix compiled and copied there, the program runs as intended. Thanks Dave!
I've modified my code in order to accommodate cogstart(), but I can't see any prime numbers that are returned. It seems to be calculating prime numbers, but always returns them as zeroes. It is as if the global variables flags, results and numbers are not being shared. Plus, the program will crash past a certain limit (semaphores not working?).
I've included a zip file. After compiling, press Enter once or twice. First it asks for the lower limit number, then it asks for the upper limit. Given those numbers, it should (but it won't) show the prime numbers in between.
Dave Hein,
Not familiar with running from command prompt. Have not done that since the old DOS days. Running windows 7.
Does your cogstart work in spin2gui ? If it does how do I load it.
I will attempt to look up the command prompt procedure, but if you walk me through it that would be great. Question does one always
have to run it from the command prompt?
Comments
(1) Couldn't you push/pop the registers to the stack? sp has to be preserved at exit of the ISR, but that doesn't mean it can never be modified. As long as all the pushes are matched by pops sp will come out the same. That way you won't need any temporary registers (the only register that'll be modified is sp).
(2) If you're using C code you don't need to explicitly save r8-r14, the compiler will do that for you if it uses them.
(3) Watch out for r15 (lr). You'll need to save that inside the ISR.
1) stack sounds much better. I'll try that. Can I use the built-in push/pop instructions? Hard to find much info on that in the manual, but one spot implies that it's only 8 words large... That doesn't help us. So I should be able to do
I'll try that soon.
2) good point. But can I trust that it will restore the registers before the reti instruction that must be manually inserted? If the compiler automatically changed the ret to reti, then I'd trust it for sure.
3) will do.
I will try rebuilding the library even after prime changes at the end. I stated the endeavor by inserting my blocks right before the orgh $400 and was getting unexpected behavior.
It just occurred to me though... Does it matter if it's above or below the comment on to of orgh $400? I was putting it above the comment, not below.
I'm not trying (at this moment... I will later) to write a shareable isr, I'm trying to make it possible to write an isr in C with p2gcc, which goes through a compiler never intended to support interrupts. Right now, only the simplest case is possible with my only working isr consisting of a drvnot and reti instruction. I know isr should stay simple... But I'd like a little more complexity than that. And even if I use nothing but inline assembly, I'll likely need to access some global variables in that assembly. At that point, propgcc is liable to clobber the registers. So, to do anything useful in an isr, I need to be able to save off the registers. Whether or not that isr will be useful to anyone else or any other application is another story.
When the compile has to build an ISR it will know what registers it will use and save them prior to modification. When it has to return it will restore those registers.
Mike
"Should" and "will" are two different things. You meant "should" . Problem is, our compiler doesn't know what an ISR is and therefore won't do anything special. Until work on PropGCC for P2 starts, the best we have is p2gcc (which has been a life saver since, if it hasn't become glaringly apparent yet, my Spin is very rusty).
Mike
I've been testing p2gcc, and some erros occur when I try to compile my (infamous) prime number calculator program. This program used to run on the P1, before being modified to the P2. Here is the source:
However, when I try to compile with p2gcc (p2gcc -v -t -s primos.c -o primos), it returns the following:
I understand that some functions, like sqrt() and atoll() are not implemented yet. However, are then any substitutions for cogstart(), lockset() and the like? I can live without the prompt asking to insert the search limits, so I can dismiss atoll() if it needs to be so. I think sqrt() can be implemented as an assembly inline, but I don't know how to do it.
Help would be appreciated.
Kind regards, Samuel Lourenço
The message "Opcode type 39 is not supported" is not very informative. What I should print is "r7 is an invalid instruction".
I believe cogstart() is supported, because it produces a coginit, which is translated from the P1 version to P2. I could provide translations for lockset and lockclr, but I haven't at this time.
The sqrt() function is a floating point function. I have worked on supporting floating point, but I haven't posted that yet. Here is a square root function that works on integers.
I haven't added fgets() to the library yet. I do have it coded up, but I haven't posted it yet. Since you are reading from stdin, you can use gets() instead.
Hi ersmit. Unfortunately, I don't have any knowledge of Spin, and hardly I know how to call assembly functions from C. Nevertheless, I'll try my best to convert this.
Hi Pilot. It is not a GUI, at least just yet. It is p2gcc that you can install via nix, or compile directly (I recommend the first option). If your Linux distro is running on a VM, make sure you have at least 4GB of RAM before fetching p2gcc via nix.
Kind regards, Samuel Lourenço
- Added ptra, ptrb, pa and pb to the list of registers in s2pasm.c. This prevents s2pasm from converting a MOV to a RDLONG/WRLONG for these registers.
- Changed the character that exits from the terminal mode in loadp2 from ESC to Ctrl-].
- Reverted back to not detecting the P2 version when the port is specified in loadp2.
Thanks.
I do not know how to string all of those files together.
There's no output unless an error occurs. A new loadp2 executable appears in that directory.
However, when I implemented the functions, it returned: I went to investigate, and found out that there was a conflict with the function names, that are defined somewhere. To solve this issue, I had to rename the lockset() and lockclr() functions. I've changed the code to this: However, the compiler still complains about unresolved symbols, namely pertaining the cogstart() function. It returns the following: This seems to be a linker issue. Is there any way we can implement cogstart()? This is getting close, BTW. Thanks!
Kind regards, Samuel Lourenço
I have run across ___umoddi3 before when I was trying to port some C code from the internet. Here is some code you can use: I haven't tested this code, but this is my best guess at how the ___umoddi3 routine works. I am using qdiv to get the remainder. qdiv does a signed division, so this code will fail for very large unsigned number. However, it should work for typical use. This routine will need to be fully implemented in the future to handle all cases.
Please, let me know when you have something.
Kind regards, Samuel Lourenço
The new files are in the attached zipfile along with cogtest.c and a runit script file. The test program blinks P56 once per second. You will need to copy prefix.spin2 to the lib directory, and then run "p2asm -o prefix.spin2" in the lib directory to assemble it. You may want to save the current prefix.spin2 in case you want to revert back to it. Run the runit script to build the test program and to run it.
Well done! Looking forward to playing with that. Thank you!
I've had some complications. I had to compile prefix.spin2 out of the destination directory, because "p2asm" does not run under sudo (it is a nix installation). But now that it has the prefix compiled and copied there, the program runs as intended. Thanks Dave!
Kind regards, Samuel Lourenço
I've modified my code in order to accommodate cogstart(), but I can't see any prime numbers that are returned. It seems to be calculating prime numbers, but always returns them as zeroes. It is as if the global variables flags, results and numbers are not being shared. Plus, the program will crash past a certain limit (semaphores not working?).
I've included a zip file. After compiling, press Enter once or twice. First it asks for the lower limit number, then it asks for the upper limit. Given those numbers, it should (but it won't) show the prime numbers in between.
Kind regards, Samuel Lourenço
Not familiar with running from command prompt. Have not done that since the old DOS days. Running windows 7.
Does your cogstart work in spin2gui ? If it does how do I load it.
I will attempt to look up the command prompt procedure, but if you walk me through it that would be great. Question does one always
have to run it from the command prompt?
Thanks
Martin
Is #include <math.h> in spin2gui??