Acessando a memória E / S mapeada está lenta

5

Eu tenho um Terasic-SoCKIT (fpga & arm cortex a9) e tenho Linux rodando no HPS. Estou tentando acessar a memória mapeada I / O, escrevi um driver de caractere simples com funções "request_mem_region" e "ioremap".

O IO mapeado na memória é um barramento AXI, usando o qual eu posso transmitir dados para o FPGA. Eu vejo que cada gravação está tomando quase 6us e para meu aplicativo eu preciso que seja menos de 1us. Além disso, o driver pára de gravar no IO mapeado depois de algumas gravações (não ver os dados sendo alterados no fpga; o buffer do driver está ficando cheio?).

A pergunta é que estou faltando alguma coisa ou porque as gravações estão acontecendo do endereço virtual para o endereço físico que não pode ser mais rápido? Se a gravação do endereço virtual está diminuindo, existe uma maneira de acelerá-lo? Eu sei que o ARM tem um DMAC, mas ainda não o explorei.

Obrigado Karthik

Desculpe, perdi a mensagem de que estava medindo o tempo no código de espaço do usuário. Mais tarde, eu verifiquei o tempo que levou para escrever no driver e foi em nanossegundos. Então, achei que a maior parte do tempo foi para a gravação do espaço do usuário no kernel.

Então, eu fiz algumas leituras adicionais e entendi que ioremap () mapeia o endereço físico para o endereço virtual do kernel e remap_pfn_range () mapeia o endereço físico / memória de E / S para o espaço virtual do usuário (isso é o que eu preciso; memória do espaço do usuário). E usei o exemplo mmap simples - link como driver do kernel. O código a seguir é o meu código do espaço do usuário:

    using namespace std;
    #include <iostream>
    #include <sys/mman.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <sys/mman.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <stdint.h>
    #include <ctime>


    #define PAGE_SIZE 4096
    #define HPS2FPGA_BRIDGE_BASE 0xc0000000
    #define BLINK_OFFSET 0x0

    volatile unsigned char *blink_mem;
    void *bridge_map;

    int main()
    {
        int fd, ret = EXIT_FAILURE;
        unsigned int i;
        unsigned char value;
        int dummy;
        off_t blink_base = HPS2FPGA_BRIDGE_BASE;
        clock_t start, stop;
        double duration;

        /* open the memory device file */
        fd = open("/dev/HPS2FPGA", O_RDWR|O_SYNC);
        if (fd < 0) {
            perror("open");
            exit(EXIT_FAILURE);
        }

        /* map the LWHPS2FPGA bridge into process memory */
        bridge_map = mmap(NULL, PAGE_SIZE, PROT_WRITE|PROT_READ|PROT_EXEC, MAP_SHARED,
                    fd, blink_base);
        if (bridge_map == MAP_FAILED) {
            perror("mmap");
            goto cleanup;
        }


        /* get the delay_ctrl peripheral's base address */
        blink_mem = (unsigned char *) (bridge_map + BLINK_OFFSET);

        start = clock();
        /* write the value */
        for(i = 0; i < 1000000; i++)
        {
            *blink_mem = i;
            dummy = *blink_mem;
        }
        stop = clock();
        duration = ( stop - start ) / (double) CLOCKS_PER_SEC;
        printf("%f", duration);

        if (munmap(bridge_map, PAGE_SIZE) < 0) {
            perror("munmap");
            goto cleanup;
        }

        ret = 0;

    cleanup:
        close(fd);
        return ret;
    }

Estou gravando no espaço de endereço virtual que mmap retorna e posso verificar a gravação lendo o valor nesse endereço, mas não vejo o valor sendo atualizado no FPGA.

Como o endereço físico é gravado quando escrevo no espaço virtual do usuário? Existe uma maneira de depurar e ver se o espaço de endereço físico está realmente sendo gravado?

    
por Karthik 22.10.2015 / 00:43

1 resposta

3

OK, parece que o assunto desta pergunta é uma furphy ... E / S de memória mapeada (concluída corretamente) será tão rápido quanto o processador pode fazê-lo para o hardware que está sendo acessado e não haverá sobrecarga para fazer isso do modo de usuário em oposição ao modo kernel (ou seja, não há "gravação do espaço de usuário no kernel"). / p>

No entanto, você ainda tem que pensar sobre o que está acontecendo quando você faz uma leitura ou escreve em um endereço (é para onde a questão foi movida). Na maioria das arquiteturas, existem dois mapeamentos - o virtual para o físico e o físico para o dispositivo. A primeira é configurada no hardware de memória virtual e a segunda é configurada no controlador de memória.

Além dos mapeamentos, todos os acessos costumam ser feitos através do hardware cache , portanto, você precisa decidir se deseja ou não o acessos a serem armazenados em cache ou não. Se o dispositivo subjacente que está sendo acessado for algum tipo de RAM, você geralmente deseja que os acessos sejam armazenados em cache. Para outros tipos de dispositivos, geralmente você não faz isso.

Pode haver muitas outras coisas em que pensar (por exemplo, se o mapeamento da VM reside no hardware da VM, a largura e o tempo de acessos, prioridade, permissões, etc.), mas o cache é o primeiro.

No caso do @ Karthik, porque ele não havia desativado cache em seu mapeamento, dependendo do tipo de cache, ou de um cache inteiro estava sendo escrita quando ele escrevia para o endereço (write-through), ou a gravação estava sendo atrasada (write-back) (se você quiser algum esclarecimento sobre cache, tente this ).

Para responder às perguntas específicas (acompanhamento), uma vez que o mapeamento de endereço virtual é feito e o cache fez seu trabalho, o acesso vai para o controlador de memória - este hardware decide qual barramento e / ou dispositivo está sendo acessado e a "coisa certa" para esse hardware, geralmente envolvendo a ativação de um chip select e / ou um write enable , talvez copiando uma parte ou todo o físico endereço para linhas de endereço , talvez algum tempo de configuração , etc.

... e a melhor maneira de depurar essas coisas é ter um analisador de algum tipo conectado ao seu dispositivo ou barramento, ou se isso for muito difícil / caro, pode haver algum suporte para depuração no controlador de memória.

Um outro ponto menor, mas importante ... anote a declaração de blink_mem no código acima - o qualificador de tipo volátil é muito importante. Diz ao compilador para não se esquivar dos acessos ao endereço. Além disso, você deve estar ciente de quaisquer instruções especiais de pipeline relacionadas aos acessos à memória (consulte o eieio em powerpc - alguém tem senso de humor: -)

Finalmente, apenas para reiterar o que foi dito nos comentários, que acabou por ser a resposta real para a pergunta, ao chamar remap_pfn_range () você desliga cache modificando as proteções de página especificadas no último argumento ( prot ) usando a macro pgprot_noncached () . Leia também este e isso e particularmente isso . Felicidades!

    
por 03.11.2015 / 06:06