Embox sur processeur Elbrus. Ou n'oublie jamais ce que tu as en intelligence

Cet article est la conclusion logique d'une série d' articles sur l' escalade d'Elbrus sur l'introduction d' Embox à l' architecture de processeur Elbrus (E2K) . Pourquoi une conclusion logique, car en conséquence, il était possible d'exécuter via telnet une application qui affiche l'image à l'écran, c'est-à-dire pour réaliser le fonctionnement complet d'Embox sur cette architecture. De nouvelles recherches peuvent difficilement être qualifiées d'introduction, bien que, bien sûr, beaucoup de choses restent floues. Et l'architecture elle-même possède de nombreuses fonctionnalités intéressantes, qui ne sont pas non plus actuellement comprises. Dans cet article, nous nous concentrerons sur l'organisation de la mémoire virtuelle, nous aborderons le PCI, parlerons un peu d'une carte réseau et toucherons une carte vidéo sur un matériel spécifique que nous avons.

Pour ceux qui sont trop paresseux pour lire l'article, je vais immédiatement donner une courte vidéo avec les résultats.


Et maintenant, pour ceux qui sont intéressés, nous allons révéler les détails techniques que nous avons pu comprendre dans le processus.

Mémoire virtuelle


Notre erreur de pile


Commençons par la mémoire virtuelle. En fait, c'est ce sur quoi nous nous sommes installés dans l' article précédent de la série. Il convient de rappeler immédiatement pourquoi nous avions besoin de mémoire virtuelle, car Embox peut fonctionner sans. C'est simple: la chose est la mise en cache. Vidyaha a fonctionné, mais j'ai dû enregistrer la même chose deux fois dans la mémoire vidéo pour une récupération d'image fiable. Bien sûr, il était possible de gérer le cache, mais nos exemples impliquent l'utilisation de la mémoire vidéo directement à partir d'une application utilisateur, sans aucun élément nucléaire tel que la gestion du cache, il était donc juste d'apprendre à mapper la mémoire comme non cache. La même chose peut être faite sous Linux en mappant fb ( exemple ).

Il convient de noter que bien que nous n'ayons pas écrit sur Elbrus depuis longtemps et qu'il puisse sembler que MMU dans cette architecture soit une sorte de super compliqué, mais la chose est différente. En fait, nous avons ajouté du soutien pendant l'été, nous n'avons tout simplement pas atteint nos mains pour écrire à ce sujet. Beaucoup de temps (plusieurs mois) a été consacré à notre stupide erreur. Cette erreur a même été commise dans le titre de l'article («Ou n'oubliez jamais ce que vous avez obtenu pendant l'intelligence»). Nous parlons de piles avec lesquelles nous avons assez bien traité et décrit cela dans l'article Climbing Elbrus - Reconnaissance. Partie technique 1. Registres, piles et autres détails techniques . " Nous avons souffert pendant très longtemps, perdant stupidement le fait que nous prenions la pile initiale (sur laquelle le système est initialisé) de quelque part à l'extérieur, et tout cartographié. ce dont nous avons besoin pour que Embox fonctionne, nous n'avons pas cartographié ces données.

Sous le chat, je vais donner une nouvelle fonction e2k_entry, décrite dans le deuxième article de l' article de la série .

Si vous le souhaitez, vous pouvez comparer.

__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]); } 

Je vais simplement expliquer que maintenant nous utilisons les fonctions context_init () et context_switch () uniquement pour basculer la pile en mémoire dans l'espace Embox. Et nous le faisons pour tous les cœurs, y compris ceux qui ne sont pas utilisés.

Organisation MMU


Je vais maintenant parler un peu de l'organisation de MMU dans l'architecture E2k.

En général, l'architecture MMU est assez ordinaire et comporte des tables à quatre niveaux (ou trois lorsque vous utilisez une page de 4 Mo).

Il existe plusieurs registres de service dans l'architecture E2k, accessibles via des commandes d'accès à des espaces alternatifs, ainsi qu'à l'espace d'E / S brièvement décrit dans l'article «Embox commence à grimper sur Elbrus» .

Nous aurons besoin de tels registres:

 #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 *// 

En fait, c'est un registre de contrôle, un registre de numéros de contexte, un registre racine de tables et un peu obscur MMU_REG_ELB_PTB. Commençons par cela, ce registre devrait avoir une certaine valeur, les 512 Go suivants seront utilisés par le processeur pour les besoins de l'équipement, et ces adresses ne seront pas disponibles pour le programmeur. Je fournirai des explications de la lettre du spécialiste ICST, je peux difficilement mieux expliquer:

Sous Linux, nous définissons MMU_ELB_PTB sur 0xff1 << 39, puis
zone supérieure de la mémoire virtuelle (0xff8000000000 - 0xffffffffffff)
réservé aux besoins d'équipements, à savoir TLB. Chaque page
page table (TS) obtient son adresse unique dans cette zone,
de plus, ces adresses sont facilement obtenues à partir de l'adresse à laquelle le programme
fait appel à la mémoire. Et depuis TLB stocke les mappages d'adresses virtuelles
physique, cela vous permet de mettre en cache dans le même tampon TLB
diffuse non seulement pour les adresses des utilisateurs, mais aussi pour le véhicule lui-même.

Dans les processeurs / architectures où des TLB séparés sont faits pour différents
niveaux de table de page, une telle astuce devient inutile.

Ainsi, lorsque vous manquez le TLB, il devient possible de ne pas lancer la recherche
à partir du niveau zéro (pgd *), et vérifier immédiatement le dernier niveau du véhicule (pte *).
Il n'y a aucun matériel requis pour cartographier cette zone elle-même, Wirth. adresses de
il n'est nécessaire que comme index pour la recherche TLB. Cependant, au cœur de
le dernier pgd du niveau zéro de la table des pages est écrit nat. l'adresse de cette
niveau le plus nul. En conséquence, seulement
les 4 derniers Ko de la zone ff80'0000'0000 - ffff'ffff'ffff - i.e. juste
niveau zéro du véhicule. Cela permet d'accéder à pgd * par un utilisateur ordinaire
lire / écrire des instructions sur des adresses virtuelles.

En conséquence, il a été décidé de simplement mettre dans ce registre une grande valeur, ce qui ne nous dérangera pas. Après tout, la solution proposée nous permet d'optimiser la recherche de pages, mais nous ne sommes pas encore engagés dans l'optimisation. Livré le même que sous Linux.

Maintenant, le registre de contrôle. Vous devez activer MMU à travers elle. Les bits connus ressemblent à ceci:

 #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 */ 

Nous nous intéressons au premier bit, qui comprend la traduction d'adresses.

Nous avons également défini _MMU_CR_SET3, mais nous n'avons pas déterminé dans quels cas particuliers cela devrait être fait.

Inscription au concours Eh bien, si c'est simple, c'est le PID du processus ou de l'espace d'adressage. Plus techniquement, il s'agit d'une extension d'adresse 11 bits. Dans notre cas, nous avons rendu toutes les pages nucléaires, en mettant le bit de globalité dans toutes nos pages, nous utilisons le même espace d'adressage et donc nous pouvons utiliser zéro dans ce registre.

Dans le registre de la table racine se trouve un pointeur vers l'adresse physique du début de la table de traduction. Vous pouvez simplement faire une fraude en mappant la table également à l'adresse spécifiée dans le registre MMU_REG_ELB_PTB, mais comme je l'ai dit, nous n'étions pas concentrés sur l'optimisation.

Que puis-je dire d'autre, la structure des tableaux est assez ordinaire, les drapeaux sont les suivants:

 #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 */ 

Pour la table à 4 niveaux, les décalages d'adresses sont les suivants:

 #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 */ 

Un peu sur PCI


Communication via des espaces d'adresses alternatifs


Avant de passer à vidyaha et à la carte réseau, revenons brièvement à PCI. Nous en avons déjà un peu parlé dans la toute première partie de "Embox commence à grimper le mont Elbrouz . " Il a montré des macros pour communiquer avec d'autres espaces d'adressage:

 #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"); \ }) 

Et il y avait une référence au principe des espaces d'adressage. Différents espaces d'adressage sont définis à l'aide du MAS (spécificateur d'adresse mémoire). Et par exemple, pour accéder à l'IO, par lequel le PCI est accessible, vous devez utiliser 6 et pour MMU 7.

Mais avec une étude plus approfondie de la macro, vous pouvez remarquer une sorte de chan_letter. Et si vous regardez la description des commandes e2k, nous trouvons
Lecture double mot LDD ddd
ldd [adresse] mas, dst

Autrement dit, à première vue, il n'y a pas de canaux. Mais si vous suivez les liens, il s'avère que le code pour l'opération donnée ldd est 67. Mais 67 est le code pour ldd uniquement pour les canaux AL0 / AL3 et AL2 / AL5, et pour les canaux AL1 / AL4 ce code correspond à l'opération POPCNTd.

Ainsi, il n'a pas été possible de comprendre pleinement quels sont les canaux dans la terminologie d'Elbrus. Je me risquerais à suggérer que cela est déjà lié au principe vliw, lorsque vous pouvez spécifier quel alu est utilisé, car dans ce type d'architecture, l'une des caractéristiques est la présence de plusieurs appareils informatiques indépendants. Je peux bien sûr me tromper, mais le fait est que pour accéder au PCI ou MMU, vous devez utiliser le deuxième ou le cinquième canal. Ainsi, la commande ressemblera à ceci:
ldd, 2 0x0, [addr_in_mas] mas_id,% reg

lspci


Je vais maintenant donner le résultat de la sortie de la commande lspci sur le périphérique que nous avons:
root @ embox: (null) #lspci
00: 0,0 (PCI dev E3E3: ABCD) [6 4]
Pont PCI-à-PCI: (null) Pont Elbrus PCIe (rév 01)
00: 1.0 (PCI dev 8086: E3E3) [6 4]
Pont PCI à PCI: Intel PCI Elbrus Virt PCI bridge (rev 01)
01: 0,0 (PCI dev 1FFF: 8000) [6 4]
Pont PCI-à-PCI: (nul) Pont Elbrus PCI (rév 05)
01: 1.0 (PCI dev 8086: 4D45) [2 0]
Contrôleur Ethernet: Intel Corporation MCST ETH1000 Gigabit Ethernet (rév 01)
01: 2.0 (PCI dev 8086: 4D49) [1 1]
Contrôleur IDE: Intel Corporation MCST IDE (rev 128)
01: 2.1 (PCI dev 8086: 0002) [7 2]
Comm simple. contrôleur: Intel Corporation (null) (rev 05)
01: 2.2 (PCI dev 8086: 8000) [7 128]
Comm simple. contrôleur: Intel Corporation Elbrus PCI bridge (rev 00)
01: 2.3 (PCI dev 1013: 6005) [4 1]
Périphérique multimédia: Cirrus Logic Crystal CS4281 PCI Audio (rev 01)
01: 3.0 (PCI dev 8086: 4748) [1 6]
Contrôleur de stockage de masse: Intel Corporation MCST SATA (rév 00)
01: 4.0 (PCI dev 8086: 554F) [12 3]
Périphérique USB: Intel Corporation OHCI pour Elbrus (rév 00)
01: 4.1 (PCI dev 8086: 5545) [12 3]
Périphérique USB: Intel Corporation EHCI pour Elbrus (rév 00)
02: 1.0 (PCI dev 126F: 0718) [3 0]
Contrôleur compatible VGA: Silicon Motion, Inc. SM718 LynxSE + (rév 160)
root @ embox: (null) #

Remarque
01: 2.2 (PCI dev 8086: 8000) [7 128]
Comm simple. contrôleur: Intel Corporation Elbrus PCI bridge (rev 00)

En fait, il s'agit d'un port série du MCST similaire à l'am85c30, au moins via cet appareil, nous communiquons via minicom.

Carte réseau


Structure générale


Passons maintenant à la carte réseau.

Si je comprends bien, il s'agit de la carte réseau d'origine, un peu similaire en fonctionnement à e1000, mais uniquement en fonctionnement (comme la présence de descripteurs dans les files d'attente de réception et de transmission).

Maintenant, plus sur les points importants que nous avons rencontrés.

Carte réseau PCI VID: PID 0x8086: 0x4D45. Ne soyez pas surpris que le VID soit le même qu'Intel, le MCST utilise souvent ce VID particulier, regardez au moins le périphérique de port série mentionné ci-dessus.

BAR0 contient une base de registres. Les registres sont les suivants:

 #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 */ 

Les trois derniers (L_E1000_PSF_CSR, L_E1000_PSF_DATA, L_E1000_INT_DELAY) que nous n'avons pas utilisés, nous n'en parlerons donc pas. Commençons par MGIO, tout est simple: lecture-écriture en utilisant le protocole MII, c'est-à-dire communication avec la puce PHY. Plus précisément, nous avons une puce DP83865.

Les procédures ne sont pas particulièrement remarquables, je vais simplement les énumérer.

Lecture:

 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; } 

Record:

 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; } 

Maintenant L_E1000_DMA_BASE_ADDR et L_E1000_E_BASE_ADDR, en fait, ils décrivent un paramètre, l'adresse du bloc de description de la carte réseau. Autrement dit, l'adresse dans Elbrus est de 64 bits et les registres sont de 32 bits.

Code actuellement:

  /* 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); /************************************************************************/ 

D'où on peut voir que L_E1000_DMA_BASE_ADDR est la partie supérieure et L_E1000_DMA_BASE_ADDR est la partie inférieure de l'adresse d'un certain bloc d'initialisation (en fait un bloc de description de carte).

La structure de description est la suivante:

 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 - ne comprenait pas, pour une raison quelconque, il est mis à zéro, nous avons fait de même.

paddr - comme vous pouvez le deviner, mac est l'adresse de la carte réseau.

rdra et tdra contiennent les adresses des anneaux des descripteurs de mémoire, les 4 bits inférieurs sont alloués à la taille de l'anneau, et c'est le logarithme de la taille. Autrement dit, s'il y a 8, alors le nombre de descripteurs dans l'anneau sera 2 ^ 8 (1 << 8 == 256).

mode est le mode de fonctionnement de la carte, les bits sont les suivants:

 #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 */ 

Autrement dit, lorsque tout est configuré, vous devez définir le bit 10. Si vous voulez un mode promiscuous, puis également 15.

Descripteurs de paquets


Maintenant sur le format des descripteurs de paquets.

A la réception:

 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 - comprendre probablement que c'est l'adresse du tampon pour le paquet
buf_length - taille du tampon
msg_length - après réception, contient la longueur du paquet reçu
statut - statut du descripteur. Lorsque le paquet est préparé et donné au DMA (carte), vous devez définir le bit 15 (RD_OWN). Si tout va bien, après avoir reçu le paquet dans ce descripteur, ce bit sera réinitialisé et 9 (RD_STP) et 8 (RD_ENP) seront définis.

Tous les bits d'état sont les suivants:

 /* 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) 

Au transfert:

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

Presque identiques à la réception, les bits d'état sont les suivants:

 /* 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) 

Lorsqu'un paquet est envoyé, il est nécessaire de définir 15 (TD_OWN), 9 (TD_STP) et 8 (TD_ENP) en conséquence. Le bit 8 signifie que c'est le dernier paquet à traiter, par conséquent, si un paquet est envoyé, vous devez installer uniquement dans le dernier.

J'ai également oublié une caractéristique importante, la longueur du tampon dans les descripteurs est écrite avec un signe moins, probablement dans du code supplémentaire. Même en petit-boutien, mais comme Elbrus a le même ordre d'octets, ce n'est probablement pas important.

Registre de gestion


Nous décrivons maintenant le dernier registre non assemblé 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 */ 

Initialisation


Il y a une séquence d'initialisation quelque peu inhabituelle:
STOP-> INIT-> IDON-> STRT

Dans ce cas, les bits RXON et TXON montent indépendamment.
Plus de détails peuvent être trouvés dans notre pilote.

Carte vidéo


Comme déjà indiqué, notre appareil utilise un vidyah Silicon Motion appelé SM718 LynxSE +. Par conséquent, tout est simple, il existe des sources de pilotes sous Linux et il n'y a rien à décrire en fait.

Eh bien, sauf que la vidéo montre qu'il s'est avéré être des images très faibles, cela ressemble à un accès lent à la mémoire. Mais ceci est sans optimisation du compilateur, et en général, c'est peut-être notre problème associé à l'utilisation incorrecte de l'architecture e2k.

Eh bien, quoi d'autre à dire sur Sakhaline Elbrus?


En principe, le temps est normal :)

Apparemment, Elbrus existe, travaille. Personnellement, je vois le principal problème du développement de cette architecture intéressante comme sa proximité. Il est difficile de croire qu'une entreprise relativement petite puisse créer un processeur, un compilateur, fournir un support et tout le reste. Oui, des développeurs de logiciels tiers ont commencé à apparaître, le même Basalt-SPO prend en charge Alt-Linux, qui peut être installé sur Elbrus .

Oui, il a été signalé que des développeurs tiers fabriquaient du matériel basé sur le processeur Elbrus, par exemple Fastwel . Mais ce ne sont là que de petites avancées vers l'ouverture. Un exemple très simple, afin de reproduire ce que nous avons dit et montré ici, nous avons besoin d'un compilateur, et seul le MCST l'a , les informations données dans l'article sont un ajout aux informations reçues de, encore une fois, le MCST , et je ne dis toujours pas qu'il est peu probable qu'un morceau de fer soit trouvé même au MCST. Il est assez ancien et ICST propose des modèles plus récents.

PS Naturellement, vous pouvez tout voir dans le référentiel Embox .

PPS Venez sur la chaîne de télégramme russe via Embox ( https://t.me/embox_chat ).

PPS Embox a mis à jour le deuxième composant de la version, désormais la version 0.4.0 actuelle

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


All Articles