Shop OBEX P1 Docs P2 Docs Learn Events
Can't Wait for PropGCC on the P2? - Page 14 — Parallax Forums

Can't Wait for PropGCC on the P2?

1111214161726

Comments

  • @DavidZemon : a few questions / points:

    (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.
  • Dave HeinDave Hein Posts: 6,347
    edited 2019-01-15 12:39
    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.
  • ersmith wrote: »
    @DavidZemon : a few questions / points:

    (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.

    3) will do.
    Dave Hein wrote: »
    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.
  • evanh wrote: »
    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.

    Mike
  • iseries wrote: »
    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" :smile:. 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).
  • It should treat the IRS as a function and therefore save all registers it uses.

    Mike
  • 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.
  • evanhevanh Posts: 15,126
    Apologies for my useless input. Excuse is I'm completely living in assembly land at the moment. Of course C should be up to the job.
  • Hi,

    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.

    Help would be appreciated.

    Kind regards, Samuel Lourenço
  • 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.
    int __attribute__((noinline)) isqrt(int x)
    {
        __asm__("qsqrt r0, #0");
        __asm__("getqx r0");
    }
    

    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
    
  • Which gui are you guys using for the "C" code?
  • samuellsamuell Posts: 554
    edited 2019-01-18 23:21
    Dave Hein wrote: »
    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.
    int __attribute__((noinline)) isqrt(int x)
    {
        __asm__("qsqrt r0, #0");
        __asm__("getqx r0");
    }
    

    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!
    ersmith wrote: »
    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.
    pilot0315 wrote: »
    Which gui are you guys using for the "C" code?
    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
  • Dave HeinDave Hein Posts: 6,347
    edited 2019-01-18 23:59
    samuell, here's a translation of Eric's code that will compile under p2gcc.
    int __attribute__((noinline)) lockclr(int id)
    {
        __asm__("lockrel r0 wc");
        __asm__("muxc    r0,##-1");
    }
    
    int __attribute__((noinline)) lockset(int id)
    {
        __asm__("locktry r0 wc");
        __asm__("muxnc   r0,##-1");
    }
    
  • Dave HeinDave Hein Posts: 6,347
    edited 2019-01-19 01:34
    I checked the following changes into GitHub.

    - 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.
  • I would like to try the loadp2. Downloaded the files. Which one starts it??
  • Dave HeinDave Hein Posts: 6,347
    edited 2019-01-19 21:54
    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.
  • evanhevanh Posts: 15,126
    edited 2019-01-20 00:50
    CloneDownload it to HDD with the "Download ZIP" button. Extract zip file and ... For loadp2 only, I do the following:
    evanh@controlled:~/hoard/p2gcc-master/loadp2_src$ sh build_linux
    

    There's no output unless an error occurs. A new loadp2 executable appears in that directory.

  • pilot0315, you have to run loadp2 from a command prompt. Are you familiar with doing that? What type of computer do you have -- Linux, Windows or Mac?
  • samuellsamuell Posts: 554
    edited 2019-01-20 02:05
    Thanks Dave,

    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!

    Kind regards, Samuel Lourenço
  • Dave HeinDave Hein Posts: 6,347
    edited 2019-01-20 02:53
    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:
    int64_t __attribute__((noinline)) __umoddi3(int64_t x, int64_t y)
    {
        __asm__("setq r1");
        __asm__("qdiv r0, r2");
        __asm__("getqy r0");
        __asm__("mov r1, #0");
    }
    
    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.
  • samuellsamuell Posts: 554
    edited 2019-01-20 04:57
    Thanks, Dave! That was very helpful. Now only cogstart() is unresolved.

    Please, let me know when you have something.

    Kind regards, Samuel Lourenço
  • Dave HeinDave Hein Posts: 6,347
    edited 2019-01-21 03:47
    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.
  • Dave Hein wrote: »
    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!
  • Hi,

    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
  • Hi,

    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
  • 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?

    Thanks
    Martin
  • Samuell,
    Is #include <math.h> in spin2gui??
Sign In or Register to comment.