- PVSM.RU - https://www.pvsm.ru -

Революционный Intel 80386 (1985 год) стал первым 32-битным процессором с архитектурой x86. Как и большинство процессоров, он содержит огромное число регистров, которые являются ключевой составляющей, обеспечивая сверхбыструю обработку данных в сравнении с основной памятью. К ним относятся регистры общего назначения, регистры индекса и селекторы сегментов, а также специальные регистры для управления памятью и разработки операционной системы. В этой статье я буду говорить о кремниевом кристалле i386 и объясню, как в нём организованы основные регистры.
Компоновка регистров в i386 оказалась на удивление сложной. Для 30 из них, которые я успел изучить, в этом процессоре используется шесть разных схем, каждая из которых оптимизирована под конкретные характеристики регистра. В некоторых ячейки совмещены для удваивания пространства хранения. Другие поддерживают доступ к данным по 8, 16 или 32 бита за раз. Регистровый файл является преимущественно трёхпортовым, то есть позволяет одновременно считывать два регистра, параллельно производя запись в третий. Наконец, меня удивило, что биты в регистрах хранятся не по порядку: младшие 16 бит каждого регистра чередуются, а старшие располагаются линейно.
На фото выше под увеличением металл-микроскопа показан блестящий кристалл i386 размером с ноготь. На снимке я обозначил основные функциональные блоки. Нас в этой статье будет интересовать Data Unit (модуль данных) в нижнем левом углу микросхемы. Он содержит 32-битный модуль АЛУ, а также блок основных регистров (в красной рамке). Его схема, называемая «тракт данных», является сердцем процессора.
Тракт данных имеет стандартную структуру: каждый регистр АЛУ представляет горизонтальную полосу, и все вместе они формируют горизонтальные группы, которые можно видеть на снимке. Эта часть преимущественно состоит из 32 одинаковых тщательно оптимизированных схем — по одной для каждого бита процессора. Все схемы имеют одинаковую ширину — 60 мкм — что позволяет собирать эти функциональные блоки вместе, подобно микроскопическим кирпичикам LEGO. Связывают их вертикальные металлические линии шины, пролегающие вдоль тракта группами по 32 штуки. Через них данные перемещаются между блоками вверх/вниз. При этом управляющие линии пролегают горизонтально, позволяя выполнять операции в АЛУ, а также чтение/запись регистров. Нестандартная часть схемы справа от Data Unit генерирует сигналы для этих управляющих линий, активируя нужные для каждой инструкции.
Тракт данных строго структурирован для достижения максимальной производительности при минимальной занимаемой на кристалле площади. Далее я расскажу, как в этой структуре реализованы регистры.
Регистры процессора — это одна из его самых видимых частей. Конкретно данная модель содержит 16 штук для прикладного программирования — очень немного по нынешним стандартам, но весьма немало по тем временам. На диаграмме ниже показано восемь 32-битных регистров общего назначения. Сверху идут EAX, EBX, ECX и EDX. Несмотря на то, что это 32-битные регистры, они также могут рассматриваться как 16- или 8-битные для обратной совместимости с более ранними процессорами. Например, к нижней половине EAX можно обращаться как к 16-битному регистру AX, а к нижнему байту EAX — как к 8-битному регистру AL. Более того, к битам с 15 по 8 можно обращаться как к 8-битному регистру AH. Иными словами, обращаться к EAX, как и к трём другим регистрам, можно четырьмя разными способами. Ниже вы увидите, что эта особенность усложняет реализацию общего набора.

Регистры общего назначения в i386. Выдержка из 80386 Programmer's Reference Manual [1], стр. 2-8
1 [2] Ну и, наконец, в этом процессоре есть куча временных регистров, которые невидимы для программиста и используются микрокодом для выполнения сложных инструкций.
Регистры i386 реализованы с использованием ячеек статической RAM, то есть схемы, которая может содержать один бит информации. Эти ячейки выстроены в сетку, формируя несколько регистров. Статическую RAM можно противопоставить динамической, которая используется в качестве основной памяти ПК. В динамической RAM каждый бит содержится в крохотном конденсаторе, а в статической используется более быстрая, но также более масштабная и сложная схема. Поскольку основная память хранит гигабайты данных, в ней динамическая RAM используется для обеспечения плотно упакованного и дешёвого хранилища. В случае регистров же ситуация иная — здесь область хранения мала, зато обеспечивается высокая скорость. То есть регистры используют статическую RAM, о которой я расскажу ниже.
Суть реализации этой памяти заключается в замыкании двух логических инверторов в петлю. Принцип работы инвертора же состоит в том, что при получении на входе 0 он выдаёт 1 и наоборот. Благодаря этому, петля инвертирования остаётся стабильной — один инвертор установлен, другой — нет. В зависимости от того, какой из инверторов установлен, схема хранит 0 или 1, как показано ниже. Таким образом пара логических элементов образует один бит памяти.

Два инвертора, связанных в петлю, могут хранить 0 или 1
2 [3] Эти сигналы передаются им по битовым шинам, которые также можно использовать для чтения хранящегося в ячейке значения.

Добавление в схему двух проходных транзисторов позволяет считывать/записывать ячейку
3 [4] Когда транзисторы открыты, сигналы по шинам записи могут проходить к инверторам. Если же они закрыты, инверторы оказываются изолированы от шин записи.
Проходные транзисторы открываются управляющим сигналом, передаваемым через «шину слов» (word line), регулирующую доступ к слову в хранилище регистра. Так как каждый инвертор состоит из двух транзисторов, вышепоказанная схема включает их шесть, почему и называется ячейкой 6T.
Ячейка 6Т использует одни и те же битовые шины для чтения и записи, поэтому писать в регистр и считывать из него одновременно нельзя. Добавление ещё двух транзисторов ведёт к получению схемы 8Т, которая уже позволяет одновременно считывать из одного регистра и записывать в другой. (В техническом смысле регистровый файл является двухпортовым). В показанной ниже схеме 8Т два дополнительных транзистора (G и H) используются для чтения.
Транзистор G буферизует значение ячейки. Он открывается, когда сигнал на выходе инвертора имеет высокий уровень, подтягивая битовую шину чтения к низкому.4 [5] Переходный транзистор H блокирует сигнал, пока из регистра происходит считывание. Управляется он шиной слов, используемой для чтения. Обратите внимание, что есть две битовых шины для записи (как раньше) и одна для чтения.

Схема хранилища ячейки. Каждый транзистор обозначен буквой
Для создания регистра (или памяти) из ячеек выстраивается сетка. Каждый ряд в этой сетке соответствует регистру, а каждый столбец — позиции бита. Горизонтальные линии — это шины слов, указывающие на нужное слово, а вертикальные — это битовые шины, передающие биты в/из регистров. Для записи вертикальные битовые шины могут передавать 32 бита (вместе с их инвертированной формой) в регистр, а для чтения — получать 32 бита из него. Что мы в итоге имеем? Каждый ряд — это регистр, данные передаются вертикально, а управляющие сигналы — горизонтально.

Ячейки статической памяти (8Т), организованные в сетку
На фото кристалла i386 ниже крупным планом показан блок регистров. Здесь мы видим сетку из ячеек хранилища, но обратите внимание, что от ряда к ряду паттерн меняется. Всего в этом блоке присутствует 30 регистров: 22 из них содержат по 32 бита, а нижние — по 16. Изучая кристалл, я выяснил, что в нём есть шесть разных групп регистров, которые я произвольно обозначил от (а) до (f). Далее я эти группы опишу.

Основной блок регистров i386 в нижней части тракта данных. Числа показывают, сколько битов регистра доступно для обращения
Начну с самой простой внизу: восемь 16-битных регистров, которые я отнёс к типу (f). Слева видно, как «обрывается» регистровый файл, поскольку объём этих регистров вдвое меньше объёма других (16 бит против 32). Эти регистры реализованы с использованием описанной выше схемы 8Т, что делает их двухпортовыми: из одного можно читать, в другой писать. Как уже говорилось, через каждый бит проходят три вертикальных шины: одна для чтения и две (с противоположной полярностью) для записи. Каждый регистр имеет две управляющие шины (шины слов): одна позволяет выбирать регистр для чтения, а другая — регистр для записи.
На фото ниже показано, как четыре ячейки типа (f) реализованы на микросхеме. На этом изображении для демонстрации внутреннего слоя кремния были удалены два металлических слоя кристалла вместе с большей частью поликремниевой разводки. Тёмными контурами обозначены области легированного кремния, а полосы вдоль этой области — это затворы транзисторов. Я отметил каждый транзистор буквой в соответствии со схемой выше. Заметьте, что схема нижней части представляет зеркальную копию верхней, что экономит много пространства. Левая и правая стороны отзеркалены с примерной точностью. Такая нестандартная форма позволяет отделить шины слов для чтения/записи, чтобы можно было управлять левой и правой половинами без коллизий.

Четыре ячейки памяти типа (f), разделённые пунктирными линиями. Небольшие неправильные квадраты — это остатки не до конца удалённого кремния
В регистровом файле и тракте данных i386 для каждого бита выделена ширина 60 мкм. Тем не менее показанная выше схема регистра необычна: хоть её ширина и составляет 60 мкм, в ней представлены две смежные ячейки регистра. Это означает, что здесь в 60 мкм втиснуто два бита, а не один. Таким образом эта плотная схема реализует по два регистра на ряд (с чередующимися битами), обеспечивая вдвое бо́льшую плотность по сравнению со схемами других регистров.
Если вам интересно, как представленные выше транзисторы соединены, на схеме ниже показано, что их физическое расположение соответствует двум из 8Т-ячеек памяти, о которых говорилось ранее. Поскольку в i386 два накладывающихся металлических слоя, разобрать детали на фото кристалла очень сложно. Желающие могут найти эти фото в моей предыдущей статье [6].

Схема из двух статических ячеек в i386, обозначенных как «R» и «L» (право и лево). Эта схема примерно совпадает с фактическим размещением деталей
Над регистрами типа (f) находятся 10 регистров типа (e), занимающие пять рядов ячеек. Эти регистры реализованы также по схеме 8Т, но при этом являются 32-битными, а не 16-ти. Поэтому, в отличие от предыдущих своих собратьев, такой регистр занимает всю ширину тракта данных. Как и ранее, в схеме двойной плотности реализуется по два регистра на ряд. Кремниевая структура здесь идентична (только имеет ширину 32 бита, а не 16), поэтому её фото включать не стану.
Над этими регистрами находятся четыре (d) других, которые уже более сложны. Это трёхпортовые регистры, то есть в процессе записи в один регистр из двух других можно выполнять чтение. (Это пригождается для операций с АЛУ, так как позволяет одновременно сложить два значения и вернуть результат). С целью поддержки чтения второго регистра для каждого бита добавляется ещё по одной вертикальной шине. В каждой ячейке есть по два дополнительных транзистора для подключения к этой новой битовой шине. Управляет этим дополнительным трактом чтения ещё одна шина слов. Поскольку каждая ячейка имеет два дополнительных транзистора, всего получается 10 транзисторов, а схема называется 10Т.

Четыре ячейки типа (d). Зелёные области с полосками — это остатки оксидных слоёв, которые были не до конца удалены — не обращайте внимания
На иллюстрации выше показаны четыре ячейки памяти типа (d). В отличие от предыдущих ячеек с двойной плотностью, каждая из этих занимает по 60 мкм. Ячейки зеркально отражаются по горизонтали и вертикали, что слегка увеличивает плотность, так как позволяет им совместно использовать шины питания. Я обозначил транзисторы буквами от А до H, как и ранее, а также отметил два дополнительных — I и J — для второй шины чтения. Схема здесь та же, что и прежде, только появилось два транзистора. Кремниевая же структура отличается сильно.
У каждого из (d) регистров есть пять управляющих линий. Две из них отвечают за выбор регистра для чтения, подключая его к одной из двух вертикальных шин. Три шины записи позволяют независимо записывать разные части регистра: старшие 16 бит, следующие 8 бит и нижние 8 бит. Это необходимо архитектуре х86, где к 32-битному регистру вроде EAX можно обращаться как к 16-битному AX, 8-битному AH или 8-битному AL. Обратите внимание, что для чтения определённой части регистра не требуется отдельных управляющих линий: регистр предоставляет все 32 бита, и отвечающая за чтение схема может игнорировать ненужные из них.
Идём выше. Три регистра (с) также построены из 10Т. Однако эти регистры не поддерживают частичную запись, поэтому записывать нужно сразу все 32 бита. В результате им требуется всего три управляющих линии (две для чтения и одна для записи). За счёт меньшего числа управляющих линий ячейки занимают меньше вертикального пространства, поэтому схема получается чуть компактнее в сравнении с предыдущими ячейками типа (d). На иллюстрации ниже показано четыре ряда типа (с) над двумя рядами типа (d). И хотя в этих ячейках также используются десять транзисторов, они чуть смещены.

Четыре ряда типа (с) над двумя ячейками типа (d)
Далее идут четыре регистра (b), которые поддерживают 16-битовую и 32-битовую запись (но не 8-битовую). Таким образом, эти регистры имеют четыре управляющих линии (две для чтения и две для записи). Из-за дополнительной управляющей линии их ячейки занимают чуть больше вертикального пространства, чем ячейки (с), но само расположение почти идентично.
Наконец, регистр (a) вверху имеет особенность: он может получать копию значения в нижележащем регистре. Это значение копируется напрямую между регистрами без использования шин чтения или записи. У этого регистра 3 управляющих линии: одна для чтения, другая для записи и третья для копирования.

Ячейка типа (а), которая может копировать значение из ячейки типа (b) ниже
5 [7] Два транзистора, S1 и S2, выступают в качестве проходных переключателей между этими инверторами и ячейкой памяти. При активации управляющей линией переключатели позволяют инверторам переписывать ячейку памяти содержимым ячейки ниже. Заметьте, что ячейка (a) из-за дополнительных транзисторов занимает значительно больше вертикального пространства.
Я так и не определил, как именно соотносятся регистры i386 с этими 30 реальными регистрами, но могу порассуждать на эту тему. Во-первых, у i386 есть четыре регистра, к которым можно обращаться как к 8-, 16- или 32-битным: EAX, EBX, ECX и EDX. Значит, они должны соответствовать регистрам (d), которые поддерживают эти паттерны доступа.
Четыре регистра индекса (ESP, EBP, ESI и EDI) могут использоваться как 32- или 16-битные, соотносясь с четырьмя регистрами (b) с теми же свойствами. Содержимое какого из этих регистров можно скопировать в регистр типа (a)? Возможно, указатель стека (ESP) копируется при обработке прерываний.
Регистровый файл содержит восемь 16-битовых регистров типа (f). В i386 есть шесть 16-битовых сегментных регистров, значит, из восьми имеющихся шесть являются сегментными, а два — дополнительными. Инструкция LOADALL [8] даёт кое-какие подсказки, указывая на то, что два дополнительных 16-битных регистра — это LDT (Local Descriptor Table, локальная таблица дескрипторов) и TR (Task Register, регистр задач). Кроме того, LOADALL обрабатывает 10 временных регистров, соответствующих 10 регистрам типа (e) в нижней части регистрового файла. Три 32-битных регистра типа © могут являться управляющим регистром CR0 и отладочными регистрами DR6 и DR7.

Шесть 16-битных сегментных регистров в i386
6 [9]
Справа от регистрового файла в сложном блоке схемы 7-битные значения используются для выбора регистров. Два значения отвечают за выбор регистров (или констант) для чтения, а третье — для записи. Сейчас я анализирую схему, которая поможет понять, как присваиваются физические регистры.
7 [10] — например, 8-битные регистры AH и AL из младших 16 бит регистра EAX. Я уже объяснял, что в регистрах используются несколько управляющих линий записи, чтобы эти разные части обновлялись по отдельности. Но есть здесь проблема и с самой схемой.
Для демонстрации этой проблемы предположим, что вы выполняете 8-битную операцию с АЛУ в регистре AH, который представляет биты 15-8 регистра EAX. Для того, чтобы использовать эти биты в операции АЛУ их нужно сдвинуть вниз на позиции 7-0, а затем при сохранении данных в AH — обратно. С другой стороны, если мы выполняем операцию АЛУ над AL (биты 7-9 в EAX), нужные биты уже находятся в правильных позициях, и сдвигать их не требуется.
8 [11]

Показанная сеть перетасовки чередует младшие 16 бит
9 [12]
10 [13] Здесь у нас возникает компромисс. Такая схема требует больше пространства в отличие от его экономии в случае заужения устройства быстрого сдвига. Похоже, что в Intel этот компромисс сочли оправданным. (Наверное, потому что сеть перетасовки требует дополнительной разводки для чередования битов, и это решение позволило избежать использования лишнего места для втискивания этой разводки).
Если вы откроете учебник по проектированию процессоров, то найдёте в нём описание создания регистров из ячеек статической памяти. Тем не менее i386 показывает, что в реальном процессоре всё гораздо сложнее. Разрабатывая 386-ю серию процессоров, в Intel для реализации регистров вместо использования одной схемы использовали шесть разных.
Причём схема регистров i386 также раскрывает проблему обратной совместимости. Архитектура x86 поддерживает доступ к 8-битным регистрам для совместимости с другими процессорами из 1971 года. Эта совместимость требует дополнительной схемы, в частности сети перетасовки и чередующихся регистров. Глядя на схему процессоров x86, не могу не отметить преимущества архитектуры RISC, которая избегает многих особенностей таких процессоров.
Если вам интересно побольше узнать о том, как реализуются ячейки памяти i386, то я уже писал более низкоуровневую статью [6]. Я планирую продолжить писать на тему этих процессоров, так что следите за обновлениями на Bluesky (@righto.com [14]) или в RSS [15].
386 Programmer's Reference Manual [16]). К ним относятся регистр глобальной таблицы дескрипторов (Global Descriptor Table Register, GDTR), регистр локальной таблицы дескрипторов (Local Descriptor Table Register, LDTR), регистр таблицы векторов прерываний (Interrupt Descriptor Table Register, IDTR) и регистр задач (Task Register, TR). Есть четыре управляющих регистра (Control Registers, CR0-CR3). Из них CR0 управляет использованием сопроцессора, страничной организацией памяти и ещё некоторыми вещами. Шесть отладочных регистров (Debug Registers) для установки аппаратных точек останова обозначаются как DR0-DR3, DR6 и DR7. Два тестовых регистра (Test Registers) для тестирования TLB называются TR6 и TR7. Я подозреваю, что в i386 эти регистры находятся в блоке сегментации (Segment Unit) и блоке страничной организации памяти (Paging Unit), а не являются частью тракта данных. ↩ [17]
↩ [18]
↩ [19]
↩ [20]
↩ [21]
подробнее [22]). И в i386 флаги наверняка реализованы аналогичным образом.
В 8086 счётчика команда (указателя инструкций) как такового не существует. Вместо этого схема упреждающей выборки команд содержит регистр, где хранится адрес текущей предвыборки. Если необходим адрес счётчика команд (к примеру, для передачи адреса возврата или выполнения относительного перехода), значение счётчика команд извлекается из адреса предвыборки. Если i386 устроен аналогично, для счётчика команд в регистровом файле не будет присутствовать физического регистра. ↩ [23]
Datapoint 2200 [24], назвав её 8008. В процессоре Intel 8008 были расширены пары регистров, поэтому BC и DE также могли использоваться в качестве 16-битных. В Intel 8086 разработчики сохранили тот же дизайн, но изменили имена 16-битных регистров на AX, BX, CX и DX, назвав их 8-битные части AH, AL и так далее. Получается, что необычная структура регистрового файла в i386 объясняется совместимостью с программируемым терминалом из 1971 года. ↩ [25]
↩ [26]
↩ [27]
↩ [28]

Часть схемы АЛУ, представленная под структурой регистра EAX
Автор: Bright_Translate
Источник [29]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/x86/421472
Ссылки в тексте:
[1] 80386 Programmer's Reference Manual: http://www.bitsavers.org/components/intel/80386/230985-001_80386_Programmers_Reference_Manual_1986.pdf#page=44
[2] 1: #anchorid1
[3] 2: #anchorid2
[4] 3: #anchorid3
[5] 4: #anchorid4
[6] предыдущей статье: https://www.righto.com/2023/11/reverse-engineering-intel-386.html
[7] 5: #anchorid5
[8] LOADALL: https://web.archive.org/web/20210624172529/https://asm.inightmare.org/opcodelst/index.php?op=LOADALL
[9] 6: #anchorid6
[10] 7: #anchorid7
[11] 8: #anchorid8
[12] 9: #anchorid9
[13] 10: #anchorid10
[14] @righto.com: https://bsky.app/profile/righto.com
[15] RSS: https://www.righto.com/feeds/posts/default
[16] 386 Programmer's Reference Manual: http://www.bitsavers.org/components/intel/80386/230985-003_386DX_Microprocessor_Programmers_Reference_Manual_1990.pdf
[17] ↩: #anchorid11
[18] ↩: #anchorid12
[19] ↩: #anchorid13
[20] ↩: #anchorid14
[21] ↩: #anchorid15
[22] подробнее: https://www.righto.com/2023/02/silicon-reverse-engineering-intel-8086.html
[23] ↩: #anchorid16
[24] Datapoint 2200: https://www.righto.com/2023/08/datapoint-to-8086.html
[25] ↩: #anchorid17
[26] ↩: #anchorid18
[27] ↩: #anchorid19
[28] ↩: #anchorid20
[29] Источник: https://habr.com/ru/companies/ruvds/articles/914144/?utm_campaign=914144&utm_source=habrahabr&utm_medium=rss
Нажмите здесь для печати.