Embox auf Elbrus-Prozessor. Oder vergiss nie, was du an Intelligenz hast

Dieser Artikel ist die logische Konsequenz aus einer Reihe von „Climbing Elbrus“ -Artikeln zur Einführung von Embox in die Elbrus-Prozessorarchitektur (E2K) . Warum eine logische Schlussfolgerung, denn als Ergebnis war es möglich, über Telnet eine Anwendung auszuführen, die das Bild auf dem Bildschirm anzeigt, dh, die volle Arbeit von Embox auf dieser Architektur zu erreichen. Weitere Forschungen können kaum als Einführung bezeichnet werden, obwohl natürlich noch vieles unklar bleibt. Und die Architektur selbst hat viele interessante Merkmale, die derzeit auch nicht verstanden werden. In diesem Artikel werden wir uns auf die Organisation des virtuellen Speichers konzentrieren, PCI ansprechen, ein wenig über eine Netzwerkkarte sprechen und eine Grafikkarte auf einer bestimmten Hardware ansprechen, über die wir verfügen.

Für diejenigen, die zu faul sind, um den Artikel zu lesen, werde ich sofort ein kurzes Video mit den Ergebnissen geben.


Und jetzt für diejenigen, die interessiert sind, werden wir die technischen Details enthüllen, die wir in dem Prozess verstehen konnten.

Virtueller Speicher


Unser Stapelfehler


Beginnen wir mit dem virtuellen Speicher. Darauf haben wir uns im vorigen Artikel der Serie geeinigt. Es lohnt sich, sich sofort daran zu erinnern, warum wir virtuellen Speicher benötigt haben, da Embox ohne diesen arbeiten kann. Es ist ganz einfach: Die Sache ist Caching. Vidyaha funktionierte, aber ich musste dasselbe zweimal im Videospeicher aufzeichnen, um ein zuverlässiges Abrufen von Bildern zu ermöglichen. Natürlich war es möglich, mit dem Cache umzugehen, aber in unseren Beispielen wurde der Videospeicher direkt von einer Benutzeranwendung aus verwendet, ohne dass Kernelemente wie die Cache-Verwaltung erforderlich waren. Daher war es richtig, zu lernen, wie Speicher als nicht cachefähig zugeordnet werden kann. Dasselbe kann unter Linux durch Zuordnen von fb ( Beispiel ) erreicht werden.

Es ist erwähnenswert, dass wir zwar lange nicht über Elbrus geschrieben haben und es den Anschein haben könnte, dass MMU in dieser Architektur eine Art superkomplizierte Sache ist, aber die Sache ist anders. Tatsächlich haben wir im Sommer Unterstützung hinzugefügt, wir haben einfach nicht unsere Hände erreicht, um darüber zu schreiben. Aufgrund unseres blöden Fehlers wurde eine lange Zeit (mehrere Monate) verbracht. Dieser Fehler wurde sogar im Titel des Artikels gemacht ("Oder vergessen Sie nie, was Sie während des Nachrichtendienstes bekommen haben"). Wir sprechen von Stacks, mit denen wir uns ziemlich gut auseinandergesetzt haben und die wir im Artikel Climbing Elbrus - Reconnaissance beschrieben haben. Technischer Teil 1. Register, Stapel und andere technische Details . " Wir haben sehr lange gelitten und dabei dummerweise die Tatsache aus den Augen verloren, dass wir den anfänglichen Stapel (auf dem das System initialisiert ist) von irgendwo außerhalb genommen und alles abgebildet haben. Damit Embox funktioniert, haben wir diese Daten nicht zugeordnet.

Unter der Katze gebe ich eine neue Funktion e2k_entry, die im zweiten Artikel des Artikels in der Reihe beschrieben wird .

Auf Wunsch können Sie vergleichen.

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

Ich werde nur erklären, dass wir jetzt die Funktionen context_init () und context_switch () nur verwenden, um den Stapel in den Speicher im Embox-Bereich zu verschieben. Und das tun wir für alle Kerne, auch für solche, die nicht verwendet werden.

MMU-Organisation


Jetzt werde ich ein wenig über die Organisation der MMU in der E2k-Architektur sprechen.

Im Allgemeinen ist die MMU-Architektur recht gewöhnlich und verfügt über Tabellen mit vier Ebenen (oder drei, wenn eine 4-MB-Seite verwendet wird).

In der E2k-Architektur gibt es mehrere Serviceregister, auf die über Zugriffsbefehle auf alternative Bereiche sowie auf den E / A-Bereich zugegriffen werden kann, der im Artikel „Embox beginnt mit dem Aufstieg auf Elbrus“ kurz beschrieben wird.

Wir werden solche Register brauchen:

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

Tatsächlich ist dies ein Steuerregister, ein Kontextnummernregister, ein Stammregister von Tabellen und ein wenig obskures MMU_REG_ELB_PTB. Beginnen wir damit, dieses Register sollte auf einen bestimmten Wert gesetzt werden, die nächsten 512 GB werden vom Prozessor für die Anforderungen des Geräts verwendet, und diese Adressen stehen dem Programmierer nicht zur Verfügung. Ich werde Erklärungen aus dem Brief des ICST-Spezialisten abgeben, ich kann es kaum besser erklären:

Unter Linux setzen wir MMU_ELB_PTB auf 0xff1 << 39 und dann
oberer Bereich des virtuellen Speichers (0xff8000000000 - 0xffffffffffff)
reserviert für die Bedürfnisse von Geräten, nämlich TLB. Jede Seite
Seitentabelle (TS) erhält seine eindeutige Adresse in diesem Bereich,
Darüber hinaus sind diese Adressen leicht von der Adresse zu erhalten, an der sich das Programm befindet
appellierte an die Erinnerung. Und seitdem TLB speichert virtuelle Adresszuordnungen
physisch, so können Sie im gleichen TLB-Puffer zwischenspeichern
sendet nicht nur für Benutzeradressen, sondern auch für das Fahrzeug selbst.

In solchen Prozessoren / Architekturen, in denen separate TLBs für verschiedene erstellt werden
Seitentabellenebenen wird ein solcher Trick unnötig.

Wenn Sie also den TLB verpassen, ist es möglich, die Suche nicht zu starten
von der Null-Ebene (pgd *) und sofort die letzte Ebene des Fahrzeugs (pte *) prüfen.
Es ist keine Hardware erforderlich, um dieses Gebiet selbst abzubilden, Wirth. Adressen von
Es wird nur als Index für die TLB-Suche benötigt. Im Kern jedoch in
Die letzte Seite in der Null-Ebene der Seitentabelle wird nat geschrieben. die adresse dafür
am Nullpunkt. Infolgedessen nur
die letzten 4 KB des Bereichs ff80'0000'0000 - ffff'ffff'ffff - d. h. genau richtig
Null Fahrzeugniveau. Dies ermöglicht den normalen Zugriff auf pgd *
Lese- / Schreibanweisungen für virtuelle Adressen.

Infolgedessen wurde beschlossen, einfach einen großen Wert in dieses Register zu setzen, der uns nicht stören wird. Schließlich können wir mit der vorgeschlagenen Lösung die Suche nach Seiten optimieren, wir haben uns jedoch noch nicht mit der Optimierung befasst. Wird wie unter Linux ausgeliefert.

Nun das Kontrollregister. Sie müssen MMU dadurch aktivieren. Bekannte Bits sehen folgendermaßen aus:

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

Wir interessieren uns für das erste Bit, das die Übersetzung von Adressen beinhaltet.

Wir haben auch _MMU_CR_SET3 gesetzt, aber wir haben nicht herausgefunden, in welchen besonderen Fällen dies geschehen soll.

Wettbewerbsregister Wenn einfach, dann ist dies die PID des Prozesses oder des Adressraums. Technisch gesehen ist dies eine 11-Bit-Adresserweiterung. In unserem Fall haben wir alle Seiten zum Kern gemacht, indem wir auf allen unseren Seiten ein bisschen Globalität eingestellt haben, den gleichen Adressraum verwenden und daher in diesem Register Null verwenden können.

Im Register der Root-Tabelle befindet sich ein Zeiger auf die physikalische Adresse des Anfangs der Übersetzungstabelle. Sie können einfach einen Betrug begehen, indem Sie die Tabelle auch der Adresse zuordnen, die im Register MMU_REG_ELB_PTB angegeben ist, aber wie gesagt, wir haben uns nicht auf die Optimierung konzentriert.

Was kann ich noch sagen, die Struktur der Tabellen ist ganz normal, die Flags sind wie folgt:

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

Für die Tabelle der 4. Ebene lauten die Adressverschiebungen wie folgt:

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

Ein bisschen über PCI


Kommunikation über alternative Adressräume


Bevor wir zu Vidyaha und der Netzwerkkarte übergehen, kehren wir kurz zu PCI zurück. Wir haben bereits im ersten Teil von "Embox beginnt mit dem Aufstieg auf den Elbrus" ein wenig darüber gesprochen. Es wurden Makros für die Kommunikation mit alternativen Adressräumen angezeigt:

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

Und es gab einen Hinweis auf das Prinzip der Adressräume. Unterschiedliche Adressräume werden mit dem MAS (Memory Address Specifier) ​​definiert. Um beispielsweise auf die E / A zuzugreifen, über die auf die PCI zugegriffen wird, müssen Sie 6 und für MMU 7 verwenden.

Bei genauerer Betrachtung des Makros können Sie jedoch eine Art chan_letter bemerken. Und wenn Sie sich die Beschreibung der e2k-Befehle ansehen, finden wir
LDD ddd Lesen von Doppelwörtern
ldd [adresse] mas, dst

Das heißt, auf den ersten Blick gibt es keine Kanäle. Wenn Sie jedoch den Links folgen, stellt sich heraus, dass der Code für die angegebene Operation ldd 67 ist. 67 ist jedoch nur der Code für ldd für die Kanäle AL0 / AL3 und AL2 / AL5, und für die Kanäle AL1 / AL4 entspricht dieser Code der Operation POPCNTd.

Daher war es nicht möglich, die Kanäle in der Terminologie von Elbrus vollständig zu verstehen. Ich würde den Vorschlag wagen, dass dies bereits mit dem vliw-Prinzip zusammenhängt, wenn Sie angeben können, welches Alu verwendet wird, da bei dieser Art von Architektur eines der Merkmale das Vorhandensein mehrerer unabhängiger Computergeräte ist. Ich kann mich natürlich irren, aber Tatsache ist, dass Sie den zweiten oder fünften Kanal verwenden müssen, um auf die PCI oder MMU zuzugreifen. Somit sieht der Befehl ungefähr so ​​aus:
ldd, 2 0x0, [addr_in_mas] mas_id,% reg

lspci


Jetzt gebe ich das Ergebnis der Ausgabe des Befehls lspci auf dem Gerät, das wir haben:
root @ embox: (null) #lspci
00: 0.0 (PCI-Entwickler E3E3: ABCD) [6 4]
PCI-zu-PCI-Brücke: (null) Elbrus PCIe-Brücke (Rev. 01)
00: 1.0 (PCI dev 8086: E3E3) [6 4]
PCI-zu-PCI-Brücke: Intel Corporation Elbrus Virt PCI-Brücke (Rev. 01)
01: 0.0 (PCI dev 1FFF: 8000) [6 4]
PCI-zu-PCI-Bridge: (null) Elbrus PCI-Bridge (Rev. 05)
01: 1.0 (PCI dev 8086: 4D45) [2 0]
Ethernet-Controller: Intel Corporation MCST ETH1000 Gigabit-Ethernet (Version 01)
01: 2.0 (PCI dev 8086: 4D49) [1 1]
IDE-Controller: Intel Corporation MCST IDE (Version 128)
01: 2.1 (PCI dev 8086: 0002) [7 2]
Einfache komm. Controller: Intel Corporation (null) (Version 05)
01: 2.2 (PCI dev 8086: 8000) [7 128]
Einfache komm. Controller: Intel Corporation Elbrus PCI-Bridge (Version 00)
01: 2.3 (PCI dev 1013: 6005) [4 1]
Multimedia-Gerät: Cirrus Logic Crystal CS4281 PCI Audio (Version 01)
01: 3.0 (PCI dev 8086: 4748) [1 6]
Massenspeicher-Controller: Intel Corporation MCST SATA (Rev. 00)
01: 4.0 (PCI dev 8086: 554F) [12 3]
USB-Gerät: Intel Corporation OHCI für Elbrus (Rev. 00)
01: 4.1 (PCI dev 8086: 5545) [12 3]
USB-Gerät: Intel Corporation EHCI für Elbrus (Rev. 00)
02: 1.0 (PCI dev 126F: 0718) [3 0]
VGA-kompatibler Controller: Silicon Motion, Inc. SM718 LynxSE + (Rev. 160)
root @ embox: (null) #

Hinweis
01: 2.2 (PCI dev 8086: 8000) [7 128]
Einfache komm. Controller: Intel Corporation Elbrus PCI-Bridge (Version 00)

Tatsächlich ist es eine serielle Schnittstelle vom MCST, ähnlich wie am85c30, zumindest über dieses Gerät, das wir über minicom kommunizieren.

Netzwerkkarte


Allgemeine Struktur


Kommen wir nun zur Netzwerkkarte.

Wenn ich das richtig verstehe, ist dies die ursprüngliche Netzwerkkarte, die im Betrieb ein bisschen der e1000 ähnelt, aber nur im Hinblick auf den Betrieb (wie das Vorhandensein von Deskriptoren in den Empfangs- und Sendewarteschlangen).

Nun mehr zu den wichtigen Punkten, auf die wir gestoßen sind.

PCI-VID der Netzwerkkarte: PID 0x8086: 0x4D45. Seien Sie nicht überrascht, dass die VID mit Intel identisch ist. Der MCST verwendet häufig diese spezielle VID. Sehen Sie sich zumindest das oben erwähnte Gerät mit serieller Schnittstelle an.

BAR0 enthält eine Registerbasis. Die Register sind wie folgt:

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

Die letzten drei (L_E1000_PSF_CSR, L_E1000_PSF_DATA, L_E1000_INT_DELAY) haben wir nicht verwendet, daher werden wir nicht darüber sprechen. Beginnen wir mit MGIO, alles ist einfach: Lesen und Schreiben mit dem MII-Protokoll, dh Kommunikation mit dem PHY-Chip. Speziell haben wir einen Chip DP83865.

Die Verfahren sind nicht besonders bemerkenswert, ich werde sie einfach auflisten.

Lesen:

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

Rekord:

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

Nun beschreiben L_E1000_DMA_BASE_ADDR und L_E1000_E_BASE_ADDR tatsächlich einen Parameter, die Adresse des Netzwerkkarten-Beschreibungsblocks. Das heißt, die Adresse in Elbrus ist 64-Bit und die Register sind 32-Bit.

Eigentlich Code:

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

Daraus ist ersichtlich, dass L_E1000_DMA_BASE_ADDR der obere Teil und L_E1000_DMA_BASE_ADDR der untere Teil der Adresse eines bestimmten Initialisierungsblocks (tatsächlich ein Kartenbeschreibungsblock) ist.

Die Beschreibungsstruktur ist wie folgt:

 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 - hat nicht verstanden, aus irgendeinem Grund ist es auf Null gesetzt, wir haben das gleiche getan.

paddr - wie Sie sich vorstellen können, ist mac die Adresse der Netzwerkkarte.

rdra und tdra enthalten die Adressen der Ringe von Speicherdeskriptoren, die unteren 4 Bits sind der Größe des Rings zugeordnet, und dies ist der Logarithmus der Größe. Das heißt, wenn es 8 gibt, beträgt die Anzahl der Deskriptoren im Ring 2 ^ 8 (1 << 8 == 256).

mode ist die Betriebsart der Karte, die Bits lauten wie folgt:

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

Das heißt, wenn alles konfiguriert ist, müssen Sie Bit 10 setzen. Wenn Sie einen Promiscuous-Modus wünschen, dann auch 15.

Paketdeskriptoren


Nun zum Format der Paketdeskriptoren.

An der rezeption:

 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 - Verstehe wahrscheinlich, dass dies die Adresse des Puffers für das Paket ist
buf_length - Puffergröße
msg_length - Enthält nach dem Empfang die Länge des empfangenen Pakets
status - Deskriptorstatus. Wenn das Paket vorbereitet und an die DMA (Karte) übergeben wurde, müssen Sie Bit 15 (RD_OWN) setzen. Wenn alles in Ordnung ist, wird dieses Bit nach dem Empfang des Pakets in diesem Deskriptor zurückgesetzt und 9 (RD_STP) und 8 (RD_ENP) werden gesetzt.

Alle Statusbits lauten wie folgt:

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

Bei Überweisung:

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

Fast wie beim Empfangen lauten die Statusbits wie folgt:

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

Wenn ein Paket gesendet wird, müssen 15 (TD_OWN), 9 (TD_STP) und 8 (TD_ENP) entsprechend eingestellt werden. Bit 8 bedeutet, dass dies das letzte zu verarbeitende Paket ist. Wenn also ein Paket gesendet wird, müssen Sie nur das letzte Paket installieren.

Ich habe auch ein wichtiges Feature vergessen, die Pufferlänge in Deskriptoren ist mit einem Minuszeichen geschrieben, wahrscheinlich in zusätzlichem Code. Auch in Little-Endian, aber da Elbrus die gleiche Bytereihenfolge haben, ist dies wahrscheinlich nicht wichtig.

Verwaltungsregister


Nun beschreiben wir das letzte nicht zusammengesetzte Register 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 */ 

Initialisierung


Es gibt eine etwas ungewöhnliche Initialisierungssequenz:
STOP-> INIT-> IDON-> STRT

In diesem Fall steigen die RXON- und TXON-Bits unabhängig voneinander an.
Weitere Details finden Sie in unserem Treiber.

Grafikkarte


Wie bereits erwähnt, verwendet unser Gerät einen Silicon Motion Vidyah namens SM718 LynxSE +. Daher ist alles einfach, es gibt Treiberquellen in Linux und es gibt eigentlich nichts zu beschreiben.

Nun, abgesehen davon, dass das Video zeigt, dass es sich als sehr niedrig erwiesen hat, fühlt es sich wie ein langsamer Zugriff auf den Speicher an. Dies ist jedoch ohne Compileroptimierung möglich, und im Allgemeinen ist dies möglicherweise unser Problem, das mit der inkorrekten Verwendung der e2k-Architektur zusammenhängt.

Was soll man noch über Sachalin Elbrus sagen?


Grundsätzlich ist das Wetter normal :)

Anscheinend existieren Elbrus, arbeiten. Persönlich sehe ich das Hauptproblem der Entwicklung dieser interessanten Architektur in ihrer Nähe. Es ist kaum zu glauben, dass ein relativ kleines Unternehmen einen Prozessor, einen Compiler, Support und alles andere erstellen kann. Ja, es tauchten Softwareentwickler von Drittanbietern auf, derselbe Basalt-SPO unterstützt Alt-Linux, das auf Elbrus installiert werden kann .

Ja, es gab Berichte, dass Drittanbieter Hardware auf der Basis des Elbrus-Prozessors herstellen, zum Beispiel Fastwel . All dies sind jedoch nur kleine Fortschritte in Richtung Offenheit. Ein sehr einfaches Beispiel, um das zu reproduzieren, was wir hier gesagt und gezeigt haben, brauchen wir einen Compiler, und nur der MCST hat ihn . Die im Artikel angegebenen Informationen sind eine Ergänzung zu den Informationen, die vom MCST erhalten wurden , und ich sage es immer noch nicht dass es unwahrscheinlich ist, dass ein Stück Eisen auch im MCST gefunden wird. Es ist ziemlich alt und ICST bietet neuere Modelle an.

PS Natürlich können Sie alles im Embox-Repository sehen .

PPS Über Embox ( https://t.me/embox_chat ) gelangen Sie zum russischen Telegrammkanal.

PPS Embox hat die zweite Komponente in der Version aktualisiert, jetzt die aktuelle Version 0.4.0

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


All Articles