Embox no processador Elbrus. Ou nunca esqueça o que você obteve em inteligência

Este artigo é a conclusão lógica de uma série de artigos sobre "escalada Elbrus" sobre a introdução da arquitetura de processador Embox à Elbrus (E2K) . Por que uma conclusão lógica, porque, como resultado, foi possível executar através do telnet um aplicativo que exibe a imagem na tela, ou seja, para obter a operação completa da Embox nessa arquitetura. Pesquisas posteriores dificilmente podem ser chamadas de introdução, embora, é claro, muito permaneça incerto. E a arquitetura em si tem muitos recursos interessantes, que também não são atualmente compreendidos. Neste artigo, focaremos na organização da memória virtual, abordaremos o PCI, falaremos um pouco sobre uma placa de rede e abordaremos uma placa de vídeo em um hardware específico que possuímos.

Para quem tem preguiça de ler o artigo, darei imediatamente um pequeno vídeo com os resultados.


E agora, para quem estiver interessado, revelaremos os detalhes técnicos que pudemos entender no processo.

Memória virtual


Nosso erro de pilha


Vamos começar com a memória virtual. Na verdade, foi nisso que decidimos no artigo anterior da série. Vale lembrar imediatamente por que precisávamos de memória virtual, porque a Embox pode funcionar sem ela. É simples: a coisa é fazer cache. Vidyaha funcionou, mas eu tive que gravar a mesma coisa duas vezes na memória de vídeo para obter uma recuperação de imagem confiável. Obviamente, era possível lidar com o cache, mas nossos exemplos envolvem o uso de memória de vídeo diretamente de um aplicativo do usuário, sem nenhum material nuclear, como gerenciamento de cache, por isso foi correto aprender como mapear a memória como não armazenável em cache. O mesmo pode ser feito no Linux, mapeando fb ( exemplo ).

Vale ressaltar que, embora não escrevamos sobre Elbrus por um longo tempo e possa parecer que o MMU nessa arquitetura seja algum tipo de super complicado, mas a coisa é diferente. De fato, adicionamos apoio no verão, simplesmente não chegamos a nossas mãos para escrever sobre isso. Passamos muito tempo (vários meses) devido ao nosso erro estúpido. Este erro foi cometido no título do artigo ("Ou nunca esqueça o que você obteve durante a inteligência"). Estamos falando de pilhas com as quais lidamos muito bem e descrevemos isso no artigo Climbing Elbrus - Reconnaissance. Parte técnica 1. Registros, pilhas e outros detalhes técnicos . " Sofremos por muito tempo, perdendo de vista estupidamente o fato de pegar a pilha inicial (na qual o sistema é inicializado) de algum lugar externo e mapear tudo. do que precisamos para a Embox funcionar, não mapeamos esses dados.

Abaixo do gato, darei uma nova função e2k_entry, descrita no segundo artigo do artigo da série .

Se desejar, você pode comparar.

__attribute__ ((__section__(".e2k_entry"))) void e2k_entry(struct pt_regs *regs) { /* Since we enable exceptions only when all CPUs except the main one * reached the idle state (cpu_idle), we can rely that order and can * guarantee exceptions happen strictly after all CPUS entries. */ if (entries_count >= CPU_COUNT) { /* Entering here because of exception or interrupt */ e2k_trap_handler(regs); RESTORE_COMMON_REGS(regs); E2K_DONE; } /* It wasn't exception, so we decide this usual program execution, * that is, Embox started on CPU0 or CPU1 */ e2k_wait_all(); entries_count = __e2k_atomic32_add(1, &entries_count); if (entries_count > 1) { /* XXX currently we support only single core */ /* Run cpu_idle on 2nd CPU */ /* it's just needed if log_debug enabled in e2k_context module * else output will be wrong because 2 cpu printing at the same time */ while(!sync_count); context_init(&cpu_ctx[0], CONTEXT_PRIVELEGED | CONTEXT_IRQDISABLE, cpu_idle, idle_stack, sizeof(idle_stack)); context_switch(&cpu_ctx_prev[0], &cpu_ctx[0]); } /* Run e2k_kernel_start on 1st CPU */ context_init(&cpu_ctx[1], CONTEXT_PRIVELEGED | CONTEXT_IRQDISABLE, e2k_kernel_start, &_stack_top, KERNEL_STACK_SZ); sync_count = __e2k_atomic32_add(1, &sync_count); context_switch(&cpu_ctx_prev[1], &cpu_ctx[1]); } 

Vou apenas explicar que agora usamos as funções context_init () e context_switch () apenas para mudar a pilha para a memória no espaço Embox. E fazemos isso para todos os núcleos, incluindo aqueles que não são usados.

Organização da MMU


Agora vou falar um pouco sobre a organização da MMU na arquitetura E2k.

Em geral, a arquitetura MMU é bastante comum e possui tabelas de quatro níveis (ou três ao usar uma página de 4 MB).

Existem vários registros de serviço na arquitetura E2k, que são acessados ​​usando comandos de acesso a espaços alternativos, bem como ao espaço de E / S descrito brevemente no artigo "Embox começa a escalar o Elbrus" .

Vamos precisar desses registros:

 #define MMU_REG_CR 0x00 /* Control register */ #define MMU_REG_CONT 0x10 /* Context register */ #define MMU_REG_CR3_RG 0x20 /* CR3 register for INTEL only */ #define MMU_REG_ELB_PTB 0x30 /* ELBRUS page table virtual base */ #define MMU_REG_ROOT_PTB 0x40 /* Root Page Table Base register *// 

Na verdade, este é um registro de controle, um registro de número de contexto, um registro raiz de tabelas e um pouco obscuro MMU_REG_ELB_PTB. Vamos começar: esse registro deve ser definido com algum valor, os próximos 512 GB serão usados ​​pelo processador para as necessidades do equipamento e esses endereços não estarão disponíveis para o programador. Fornecerei explicações da carta do especialista em ICST, mal posso explicar melhor:

No Linux, configuramos MMU_ELB_PTB para 0xff1 << 39 e, em seguida,
área superior da memória virtual (0xff8000000000 - 0xffffffffffff)
reservado para as necessidades de equipamentos, ou seja, TLB. Cada página
A tabela de páginas (TS) obtém seu endereço exclusivo nessa área,
além disso, esses endereços são facilmente obtidos no endereço em que o programa
apelou para a memória. E desde TLB armazena mapeamentos de endereços virtuais
físico, isso permite armazenar em cache no mesmo buffer TLB
transmite não apenas para endereços de usuários, mas também para o próprio veículo.

Nos processadores / arquiteturas em que TLBs separados são feitos para diferentes
níveis da tabela de páginas, esse truque se torna desnecessário.

Assim, quando você perde o TLB, torna-se possível não iniciar a pesquisa
do nível zero (pgd *) e verifique imediatamente o último nível do veículo (pte *).
Não há nenhum requisito de hardware para mapear essa área em si, Wirth. endereços de
é necessário apenas como índices para pesquisa TLB. No entanto, no cerne da
o último pgd no nível zero da tabela de páginas é escrito nat. o endereço deste
nível mais zero. Como resultado, apenas
os últimos 4 KB da área ff80'0000'0000 - ffff'ffff'ffff - ou seja, apenas certo
nível zero do veículo. Isso permite que o pgd * seja acessado por
ler / escrever instruções trabalhando em endereços virtuais.

Como resultado, decidiu-se simplesmente colocar neste registro um grande valor, o que não nos incomodará. Afinal, a solução proposta nos permite otimizar a pesquisa de páginas, mas ainda não estamos envolvidos na otimização. Entregue o mesmo que no Linux.

Agora, o registro de controle. Você precisa habilitar o MMU através dele. Os bits conhecidos são assim:

 #define _MMU_CR_TLB_EN 0x0000000000000001 /* translation enable */ #define _MMU_CR_CD_MASK 0x0000000000000006 /* cache disable bits */ #define _MMU_CR_SET1 0x0000000000000008 /* set #1 enable for 4 MB pages */ #define _MMU_CR_SET2 0x0000000000000010 /* set #2 enable for 4 MB pages */ #define _MMU_CR_SET3 0x0000000000000020 /* set #3 enable for 4 MB pages */ /* paging enable for second space INTEL */ #define _MMU_CR_CR0_PG 0x0000000000000040 /* page size 4Mb enable for second space INTEL */ #define _MMU_CR_CR4_PSE 0x0000000000000080 /* cache disable for secondary space INTEL */ #define _MMU_CR_CR0_CD 0x0000000000000100 /* TLU enable for secondary space INTEL */ #define _MMU_CR_TLU2_EN 0x0000000000000200 /* memory protection table enable for LD from secondary space INTEL */ #define _MMU_CR_LD_MPT 0x0000000000000400 #define _MMU_CR_IPD_MASK 0x0000000000000800 /* Instruction Prefetch Depth */ #define _MMU_CR_UPT_EN 0x0000000000001000 /* enable UPT */ 

Estamos interessados ​​no primeiro bit, que inclui a tradução de endereços.

Também definimos _MMU_CR_SET3, mas ainda não descobrimos em quais casos específicos isso deve ser feito.

Registo no concurso Bem, se simples, esse é o PID do processo ou espaço de endereço. Mais tecnicamente, essa é uma extensão de endereço de 11 bits. No nosso caso, tornamos todas as páginas nucleares, definindo o pouco de globalidade em todas as nossas páginas, usamos o mesmo espaço de endereço e, portanto, podemos usar zero nesse registro.

No registro da tabela raiz, há um ponteiro para o endereço físico do início da tabela de conversão. Você pode simplesmente fazer uma fraude mapeando a tabela também para o endereço especificado no registro MMU_REG_ELB_PTB, mas como eu disse, não estávamos focados na otimização.

O que mais posso dizer, a estrutura das tabelas é bastante comum, os sinalizadores são os seguintes:

 #define E2K_MMU_PAGE_P 0x0000000000000001ULL /* Page Present bit */ #define E2K_MMU_PAGE_W 0x0000000000000002ULL /* Writable (0 - only read) */ #define E2K_MMU_PAGE_UU2 0x0000000000000004ULL /* unused bit # 2 */ #define E2K_MMU_PAGE_PWT 0x0000000000000008ULL /* Write Through */ #define E2K_MMU_PAGE_CD1 0x0000000000000010ULL /* Cache disable (right bit) */ #define E2K_MMU_PAGE_A 0x0000000000000020ULL /* Accessed Page */ #define E2K_MMU_PAGE_D 0x0000000000000040ULL /* Page Dirty */ #define E2K_MMU_PAGE_HUGE 0x0000000000000080ULL /* Page Size */ #define E2K_MMU_PAGE_G 0x0000000000000100ULL /* Global Page */ #define E2K_MMU_PAGE_CD2 0x0000000000000200ULL /* Cache disable (left bit) */ #define E2K_MMU_PAGE_NWA 0x0000000000000400ULL /* Prohibit address writing */ #define E2K_MMU_PAGE_AVAIL 0x0000000000000800ULL /* Available page */ #define E2K_MMU_PAGE_PFN 0x000000fffffff000ULL /* Physical Page Number */ #define E2K_MMU_PAGE_VALID 0x0000010000000000ULL /* Valid Page */ #define E2K_MMU_PAGE_PV 0x0000020000000000ULL /* PriVileged Page */ #define E2K_MMU_PAGE_INT_PR 0x0000040000000000ULL /* Integer address access Protection */ #define E2K_MMU_PAGE_NON_EX 0x0000080000000000ULL /* Non Executable Page */ #define E2K_MMU_PAGE_RES 0x0000f00000000000ULL /* Reserved bits */ #define E2K_MMU_PAGE_C_UNIT 0xffff000000000000ULL /* Compilation Unit */ 

Para a tabela de quatro níveis, as mudanças de endereço são as seguintes:

 #define __MMU_PGD_SHIFT (PAGE_SHIFT + 3 * (PAGE_SHIFT-3)) /* 39 */ #define __MMU_PUD_SHIFT (PAGE_SHIFT + 2 * (PAGE_SHIFT-3)) /* 30 */ #define __MMU_PMD_SHIFT (PAGE_SHIFT + 1 * (PAGE_SHIFT-3)) /* 21 */ 

Um pouco sobre PCI


Comunicação através de espaços de endereço alternativos


Antes de passar para o vidyaha e a placa de rede, voltemos brevemente ao PCI. Já conversamos um pouco sobre isso na primeira parte de "A Embox começa a escalar o Monte Elbrus" . Ele mostrou macros para comunicação com espaços de endereço alternativos:

 #define _E2K_READ_MAS(addr, mas, type, size_letter, chan_letter) \ ({ \ register type res; \ asm volatile ("ld" #size_letter "," #chan_letter " \t0x0, [%1] %2, %0" \ : "=r" (res) \ : "r" ((__e2k_ptr_t) (addr)), \ "i" (mas)); \ res; \ }) #define _E2K_WRITE_MAS(addr, val, mas, type, size_letter, chan_letter) \ ({ \ asm volatile ("st" #size_letter "," #chan_letter " \t0x0, [%0] %2, %1" \ : \ : "r" ((__e2k_ptr_t) (addr)), \ "r" ((type) (val)), \ "i" (mas) \ : "memory"); \ }) 

E havia uma referência ao princípio dos espaços de endereço. Diferentes espaços de endereço são definidos usando o MAS (especificador de endereço de memória). E, por exemplo, para acessar o IO, através do qual o PCI é acessado, você precisa usar 6 e o ​​MMU 7.

Mas com um estudo mais aprofundado da macro, você pode notar algum tipo de carta_de_ chan. E se você olhar para a descrição dos comandos e2k, encontramos
LDD ddd leitura de duas palavras
ldd [address] mas, dst

Ou seja, à primeira vista, não há canais. Mas, se você seguir os links, o código para a operação ldd especificada é 67. Mas 67 é o código para ldd apenas para os canais AL0 / AL3 e AL2 / AL5, e para os canais AL1 / AL4, esse código corresponde à operação POPCNTd.

Portanto, não foi possível entender completamente quais canais estão na terminologia de Elbrus. Atrevo-me a sugerir que isso já está conectado ao princípio vliw, quando você pode especificar qual alu é usada, porque nesse tipo de arquitetura um dos recursos é a presença de vários dispositivos de computação independentes. É claro que posso estar enganado, mas o fato é que, para acessar o PCI ou MMU, é necessário usar o segundo ou o quinto canal. Assim, o comando será algo como isto:
ldd, 2 0x0, [addr_in_mas] mas_id,% reg

lspci


Agora vou dar o resultado da saída do comando lspci no dispositivo que temos:
root @ embox: (nulo) #lspci
00: 0.0 (PCI dev E3E3: ABCD) [6 4]
Ponte PCI-to-PCI: (nula) ponte Elbrus PCIe (rev 01)
00: 1.0 (PCI dev 8086: E3E3) [6 4]
Ponte PCI-to-PCI: ponte Intel Corporation Elbrus Virt PCI (rev 01)
01: 0.0 (PCI dev 1FFF: 8000) [6 4]
Ponte PCI-para-PCI: (nula) Ponte PCI Elbrus (rev 05)
01: 1.0 (PCI dev 8086: 4D45) [2 0]
Controlador Ethernet: Intel Corporation MCST ETH1000 Gigabit Ethernet (rev 01)
01: 2.0 (PCI dev 8086: 4D49) [1 1]
Controlador IDE: Intel Corporation MCST IDE (rev 128)
01: 2.1 (PCI dev 8086: 0002) [7 2]
Comunicação simples. controlador: Intel Corporation (nulo) (rev 05)
01: 2.2 (PCI dev 8086: 8000) [7128]
Comunicação simples. controlador: ponte PCI Intel Corporation Elbrus (rev 00)
01: 2.3 (PCI dev 1013: 6005) [4 1]
Dispositivo multimídia: Cirrus Logic Crystal CS4281 PCI Audio (rev 01)
01: 3.0 (dev 8086: 4748 do PCI) [1 6]
Controlador de armazenamento em massa: Intel Corporation MCST SATA (rev 00)
01: 4.0 (PCI dev 8086: 554F) [12 3]
Dispositivo USB: Intel Corporation OHCI para Elbrus (rev 00)
01: 4.1 (PCI dev 8086: 5545) [12 3]
Dispositivo USB: Intel Corporation EHCI para Elbrus (rev 00)
02: 1.0 (PCI dev 126F: 0718) [3 0]
Controlador compatível com VGA: Silicon Motion, Inc. SM718 LynxSE + (rev 160)
root @ embox: (nulo) #

Nota
01: 2.2 (PCI dev 8086: 8000) [7128]
Comunicação simples. controlador: ponte PCI Intel Corporation Elbrus (rev 00)

De fato, é uma porta serial do MCST semelhante à am85c30, pelo menos através deste dispositivo que nos comunicamos através do minicom.

Placa de rede


Estrutura geral


Agora vamos para a placa de rede.

Se bem entendi, essa é a placa de rede original, um pouco semelhante em operação ao e1000, mas apenas em operação (como a presença de descritores nas filas de recebimento e transmissão).

Agora, mais sobre os pontos importantes que encontramos.

Placa de rede PCI VID: PID 0x8086: 0x4D45. Não se surpreenda que o VID seja o mesmo da Intel, o MCST geralmente usa esse VID em particular; veja pelo menos o dispositivo da porta serial mencionado acima.

BAR0 contém uma base de registro. Os registros são os seguintes:

 #define L_E1000_E_CSR 0x00 /* Ethernet Control/Status Register */ #define L_E1000_MGIO_CSR 0x04 /* MGIO Control/Status Register */ #define L_E1000_MGIO_DATA 0x08 /* MGIO Data Register */ #define L_E1000_E_BASE_ADDR 0x0c /* EthernetBase Address Register */ #define L_E1000_DMA_BASE_ADDR 0x10 /* DMA Base Address Register */ #define L_E1000_PSF_CSR 0x14 /* Pause Frame Control/Status Register */ #define L_E1000_PSF_DATA 0x18 /* Pause Frame Data Register */ #define L_E1000_INT_DELAY 0x1c /* Interrupt Delay Register */ 

Os três últimos (L_E1000_PSF_CSR, L_E1000_PSF_DATA, L_E1000_INT_DELAY) que não usamos, portanto não falaremos sobre eles. Vamos começar com o MGIO, tudo é simples: leitura e gravação usando o protocolo MII, ou seja, comunicação com o chip PHY. Especificamente, temos um chip DP83865.

Os procedimentos não são particularmente notáveis, simplesmente os listarei.

Leitura:

 static int e1000_mii_readreg(struct net_device *dev, int phy_id, int reg_num) { struct l_e1000_priv *ep = netdev_priv(dev); uint32_t rd; uint16_t val_out = 0; int i = 0; rd = 0; rd |= 0x2 << MGIO_CS_OFF; rd |= 0x1 << MGIO_ST_OF_F_OFF; rd |= 0x2 << MGIO_OP_CODE_OFF; /* Read */ rd |= (phy_id & 0x1f) << MGIO_PHY_AD_OFF; rd |= (reg_num & 0x1f) << MGIO_REG_AD_OFF; e1000_write_mgio_data(ep, rd); rd = 0; for (i = 0; i != 1000; i++) { if (e1000_read_mgio_csr(ep) & MGIO_CSR_RRDY) { rd = (uint16_t)e1000_read_mgio_data(ep); val_out = rd & 0xffff; log_debug("reg 0x%x >>> 0x%x", reg_num, val_out); return val_out; } usleep(100); } log_error("mdio_read: Unable to read from MGIO_DATA reg\n"); return val_out; } 

Registro:

 static void e1000_mii_writereg(struct net_device *dev, int phy_id, int reg_num, int val) { struct l_e1000_priv *ep = netdev_priv(dev); uint32_t wr; int i = 0; wr = 0; wr |= 0x2 << MGIO_CS_OFF; wr |= 0x1 << MGIO_ST_OF_F_OFF; wr |= 0x1 << MGIO_OP_CODE_OFF; /* Write */ wr |= (phy_id & 0x1f) << MGIO_PHY_AD_OFF; wr |= (reg_num & 0x1f) << MGIO_REG_AD_OFF; wr |= val & 0xffff; log_debug("reg 0x%x <<< 0x%x", reg_num, val); e1000_write_mgio_data(ep, wr); for (i = 0; i != 1000; i++) { if (e1000_read_mgio_csr(ep) & MGIO_CSR_RRDY) { return; } usleep(100); } log_error("Unable to write MGIO_DATA reg: val = 0x%x", wr); return; } 

Agora L_E1000_DMA_BASE_ADDR e L_E1000_E_BASE_ADDR, na verdade, eles descrevem um parâmetro, o endereço do bloco de descrição da placa de rede. Ou seja, o endereço no Elbrus é de 64 bits e os registros são de 32 bits.

Na verdade codifique:

  /* low 32 bits */ init_block_addr_part = (uint32_t)((uintptr_t)ep->init_block & 0xffffffff); e1000_write_e_base_addr(ep, init_block_addr_part); log_debug("Init Block Low DMA addr: 0x%x", init_block_addr_part); /* high 32 bits */ init_block_addr_part = (uint32_t)(((uintptr_t)(ep->init_block) >> 32) & 0xffffffff); e1000_write_dma_base_addr(ep, init_block_addr_part); log_debug("Init Block High DMA addr: 0x%x", init_block_addr_part); /************************************************************************/ 

A partir do qual é possível ver que L_E1000_DMA_BASE_ADDR é a parte superior e L_E1000_DMA_BASE_ADDR é a parte inferior do endereço de um determinado bloco de inicialização (na verdade, um bloco de descrição de cartões).

A estrutura da descrição é a seguinte:

 struct l_e1000_init_block { uint16_t mode; uint8_t paddr[6]; uint64_t laddrf; /* 31:4 = addr of rx desc ring (16 bytes align) + * 3:0 = number of descriptors (the power of two) * 0x09 is max value (desc number = 512 if [3:0] >= 0x09) */ uint32_t rdra; /* 31:4 = addr of tx desc ring (16 bytes align) + * 3:0 = number of descriptors (the power of two) * 0x09 is max value (desc number = 512 if [3:0] >= 0x09) */ uint32_t tdra; } __attribute__((packed)); 

C laddrf - não entendi, por algum motivo, é colocado a zero, fizemos o mesmo.

paddr - como você pode imaginar, mac é o endereço da placa de rede.

rdra e tdra contêm os endereços dos anéis dos descritores de memória, os 4 bits inferiores são alocados ao tamanho do anel e esse é o logaritmo do tamanho. Ou seja, se houver 8, o número de descritores no anel será 2 ^ 8 (1 << 8 == 256).

mode é o modo de operação do cartão, os bits são os seguintes:

 #define DRX (1 << 0) /* Receiver disable */ #define DTX (1 << 1) /* Transmitter disable */ #define LOOP (1 << 2) /* loopback */ #define DTCR (1 << 3) /* disable transmit crc */ #define COLL (1 << 4) /* force collision */ #define DRTY (1 << 5) /* disable retry */ #define INTL (1 << 6) /* Internal loopback */ #define EMBA (1 << 7) /* enable modified back-off algorithm */ #define EJMF (1 << 8) /* enable jambo frame */ #define EPSF (1 << 9) /* enable pause frame */ #define FULL (1 << 10) /* full packet mode */ #define PROM (1 << 15) /* promiscuous mode */ 

Ou seja, quando tudo estiver configurado, você precisará definir o bit 10. Se desejar um modo promíscuo, também 15.

Descritores de pacotes


Agora, sobre o formato dos descritores de pacotes.

Na recepção:

 struct l_e1000_rx_desc { uint32_t base; int16_t buf_length; int16_t status; int16_t msg_length; uint16_t reserved1; uint32_t etmr; } __attribute__((packed)); 

base - provavelmente entenda que este é o endereço do buffer para o pacote
buf_length - tamanho do buffer
msg_length - depois de receber, contém o comprimento do pacote recebido
status - status do descritor. Quando o pacote é preparado e entregue ao DMA (cartão), você precisa definir o bit 15 (RD_OWN). Se tudo estiver bem, depois de receber o pacote neste descritor, esse bit será redefinido e 9 (RD_STP) e 8 (RD_ENP) serão configurados.

Todos os bits de status são os seguintes:

 /* RX Descriptor status bits */ #define RD_OWN (1 << 15) #define RD_ERR (1 << 14) #define RD_FRAM (1 << 13) #define RD_OFLO (1 << 12) #define RD_CRC (1 << 11) #define RD_BUFF (1 << 10) #define RD_STP (1 << 9) #define RD_ENP (1 << 8) #define RD_PAM (1 << 6) #define RD_LAFM (1 << 4) #define RD_BAM (1 << 3) 

Na transferência:

 struct l_e1000_tx_desc { uint32_t base; int16_t buf_length; int16_t status; uint32_t misc; uint32_t etmr; } __attribute__((packed)); 

Quase o mesmo que receber, os bits de status são os seguintes:

 /* TX Descriptor status bits */ #define TD_OWN (1 << 15) #define TD_ERR (1 << 14) #define TD_AFCS (1 << 13) #define TD_NOINTR (1 << 13) #define TD_MORE (1 << 12) #define TD_ONE (1 << 11) #define TD_DEF (1 << 10) #define TD_STP (1 << 9) #define TD_ENP (1 << 8) 

Quando um pacote é enviado, é necessário definir 15 (TD_OWN), 9 (TD_STP) e 8 (TD_ENP) de acordo. O bit 8 significa que este é o último pacote a ser processado; portanto, se um pacote for enviado, você precisará instalar apenas o último.

Também esqueci um recurso importante, o tamanho do buffer nos descritores é escrito com um sinal de menos, provavelmente em código adicional. Mesmo em little-endian, mas como a Elbrus tem a mesma ordem de bytes, isso provavelmente não é importante.

Registo de gestão


Agora descrevemos o último registro desmontado L_E1000_E_CSR:

 /* E_CSR register bits */ /* 31:21 unused, readed as 0 */ #define E_CSR_ATME (1 << 24) /* RW, Add Timer Enable */ #define E_CSR_TMCE (1 << 23) /* RW, Timer Clear Enable */ #define E_CSR_DRIN (1 << 22) /* RW, Disable RX Interrupt */ #define E_CSR_DTIN (1 << 21) /* RW, Disable TX Interrupt */ #define E_CSR_ESLE (1 << 20) /* RW, Enable Slave Error */ #define E_CSR_SLVE (1 << 19) /* RW1c, Slave Error */ #define E_CSR_PSFI (1 << 18) /* RW1c, Pause Frame Interrupt */ /* 17 unused, read as 0 */ #define E_CSR_SINT (1 << 16) /* R, Status Interrupt */ #define E_CSR_ERR (1 << 15) /* R, Error */ #define E_CSR_BABL (1 << 14) /* RW1c, Babble */ #define E_CSR_CERR (1 << 13) /* RW1c, Collision Error */ #define E_CSR_MISS (1 << 12) /* RW1c, Missed Packet */ #define E_CSR_MERR (1 << 11) /* RW1c, Memory Error */ #define E_CSR_RINT (1 << 10) /* RW1c, Receiver Interrupt */ #define E_CSR_TINT (1 << 9) /* RW1c, Transmiter Interrupt */ #define E_CSR_IDON (1 << 8) /* RW1c, Initialization Done */ #define E_CSR_INTR (1 << 7) /* R, Interrupt Flag */ #define E_CSR_INEA (1 << 6) /* RW, Interrupt Enable */ #define E_CSR_RXON (1 << 5) /* R, Receiver On */ #define E_CSR_TXON (1 << 4) /* R, Transmiter On */ #define E_CSR_TDMD (1 << 3) /* RW1, Transmit Demand */ #define E_CSR_STOP (1 << 2) /* RW1, Stop */ #define E_CSR_STRT (1 << 1) /* RW1, Start */ #define E_CSR_INIT (1 << 0) /* RW1, Initialize */ 

Inicialização


Há uma sequência de inicialização um tanto incomum:
PARAR-> INIT-> IDON-> STRT

Nesse caso, os bits RXON e TXON aumentam independentemente.
Mais detalhes podem ser encontrados em nosso driver.

Placa de vídeo


Como já observado, nosso dispositivo usa um vidyah da Silicon Motion chamado SM718 LynxSE +. Portanto, tudo é simples, existem fontes de driver no Linux e não há nada para descrever realmente.

Bem, exceto pelo fato de o vídeo mostrar que ficou muito baixo em fps, parece um acesso lento à memória. Mas isso não tem otimização do compilador e, em geral, talvez esse seja o nosso problema associado ao uso incorreto da arquitetura e2k.

Bem, o que mais dizer sobre Sakhalin Elbrus?


Em princípio, o tempo está normal :)

Aparentemente, Elbrus existe, trabalha. Pessoalmente, vejo o principal problema do desenvolvimento dessa arquitetura interessante como sua proximidade. É difícil acreditar que uma empresa relativamente pequena possa criar um processador, um compilador, fornecer suporte e tudo mais. Sim, começaram a aparecer desenvolvedores de software de terceiros, o mesmo Basalt-SPO suporta o Alt-Linux, que pode ser instalado no Elbrus .

Sim, houve relatos de que desenvolvedores de terceiros estão fabricando hardware baseado no processador Elbrus, por exemplo, Fastwel . Mas todos esses são apenas pequenos avanços em direção à abertura. Um exemplo muito simples, para reproduzir o que dissemos e mostramos aqui, precisamos de um compilador, e somente o MCST o possui , as informações fornecidas no artigo são um complemento às informações recebidas do MCST , e ainda não digo que é improvável que um pedaço de ferro seja encontrado mesmo no MCST. É bastante antigo e o ICST oferece modelos mais recentes.

PS Naturalmente, você pode ver tudo no repositório Embox .

PPS Venha para o canal de telegrama russo via Embox ( https://t.me/embox_chat ).

O PPS Embox atualizou o segundo componente da versão, agora o atual 0.4.0

Source: https://habr.com/ru/post/pt485694/


All Articles