All streams
Search
Write a publication
Pull to refresh
69
0.8
Иван Савватеев @SIISII

Микроконтроллеры, цифровая электроника, ОС…

Send message

Мне кажется, автор просто что-то там недопонял. Недостаточное понимание видно, например, из "описания" MMU -- ведь принципиально и на ARM, и на IA-32, и почти в любых других архитектурах MMU работают одинаково -- точно так же, как ещё в Системе 370 (несколько уровней таблиц переадресации в памяти, базовый адрес таблицы верхнего уровня -- в некоем специальном регистре, а для ускорения преобразования -- TLB).

Инструкции работают со скалярными или векторными регистрами, если надо обработать большой массив, нужен цикл. И да, сейчас видимые отличие CISC/RISC скорее в наличии/отсутствии аргументов в памяти в арифметических инструкциях. Хотя сейчас в ARM есть, скажем, CISC-style атомики, инкрементирующие значение в памяти одной инструкцией, в спецификации (в железе пока не видел) есть и инструкции копирования памяти по типу REP MOVS в x86.

Кстати говоря, для реализации атомарных операций вроде И, ИЛИ шина AXI имеет соответствующие навороты, чтобы операции эти фактически выполнялись контроллером памяти, а не процессором. Откопал, когда смотрел сию шину (использую в своих мегапроектах на ПЛИС, поскольку она используется для подключения контроллера памяти).

В общем, похоже, от изначальных RISCов теперь не осталось ничего. Ну а микрокод высокой производительности ни разу не противоречит, как и я говорил, и всякие там Интелы на практике доказывали. Как по мне, бороться с ним не надо -- он даёт гибкость (медленная, но компактная реализация тоже может быть ценной -- а использование микропрограммного управления даёт возможность легко подстраиваться под потребности). В общем, исходить, смотря по задаче.

  1. Про RISC-V не знал -- по большому счёту, я с ним не знаком, хотя всё собираюсь заняться. В известных же мне типа RISCах подобные операции (шифрование, например) выполняются железными блоками, которые расположены отдельно от процессора и, по сути, выступают в роли внешних устройств, т.е. частью соответствующей архитектуры не являются. Таковы, например, многие микроконтроллеры ARM: сама система команд никаких тебе шифраций или, скажем, преобразования UTF-8 в UTF-32 или обратно не содержит.

  2. Ну и опять повторюсь, что от изначальной идеи RISC (а она -- именно что небольшое количество простых команд) отказались, считай, полностью -- она оказалась неконкурентоспособной с CISCами, как только совершенствование технологий дало возможность конвейерный (а позже и суперскалярный) CISC впихнуть на один кристалл. Так что лично для меня сейчас RISC -- это архитектура, не имеющая команд обработки данных прямо в памяти, ибо это, по сути, единственное, что нынешние RISCи отличает от нынешних CISCов, хотя и это различие, похоже, постепенно уходит (ведь, полагаю, RISC-V шифрует данные в памяти? Регистров у него не так уж много, и большой объём в них не впихнуть. Надо как-нибудь изучить сей вопрос...)

Если говорить про архитектуры (в первом приближении -- про системы команд), то никаким гибридом i486 не является -- самый натуральный (и плохой) CISC.

Если же говорить про внутреннее устройство, то, как я уже написал, конвейеризация появилась задолго до появления RISC, и одно с другим не связано от слова "совсем".

Ну, это именно ошибка в чистом виде, а не "фишка" (особенность микроархитектуры), которая превращается в уязвимость.

  • Так как инструкции могли быть очень длинными, они требовали больше транзисторов, что повышало энергопотребление процессора, что было большой проблемой для отрасли 3д-моделирования и рендеринга того времени;

  • Для декодирования и выполнения нескольких сложных операций процессору требовалось много памяти, которая на тот момент стоила очень дорого (к примеру, один мегабайт оперативной памяти стоил около 5000 долларов). Также при обращении декодера к памяти скорость обработки инструкций становилась меньше;

  • Большинство возможностей CISC (например, строковые операции) редко использовались, но усложняли декодер.

Строго говоря, неверны все три пункта.

Количество потребных транзисторов со сложностью системы команд связано отнюдь не линейным образом. В частности, сложность исполнительных блоков (условно говоря, АЛУ) для выполнения одних и тех же операций вообще никак не зависит от кодирования команд как такового; нет никакой разницы и в конструкциях кэшей, TLB, регистровых файлов и т.д. и т.п.

С декодированием тоже далеко не всё однозначно. Когда автор статьи пишет про длину команды от 1 до 15 байт, он, несомненно, имеет в виду IA-32 aka x86. Эта кодировка не самая длинная (у VAX-11, если память не изменяет, длина команды могла достигать вообще 36 байт), но одна из самых бредовых и сложных для декодирования -- в первую очередь, из-за наличия префиксов, ни количество, ни порядок которых никак архитектурой не ограничены. Однако CISC отнюдь не обязан быть столь безумным. Скажем, такой классический CISC, как IBM System/360 (и все её потомки, включая современную z/Architecture), имеет длину команды 2, 4 или 6 байт, причём длина однозначно устанавливается анализом двух старших битов первого байта -- т.е. даже проще, чем, скажем, для команд набора Thumb-2 архитектуры ARM (там всего две длины -- 2 или 4 байта, -- но анализ длины сложнее).

Насчёт памяти написана вообще полная ерунда -- CISC как раз требует меньше памяти, поскольку в одной команде может закодировать куда более сложную операцию. Сколько места займёт подпрограмма шифрования AES на ARM или RISC-V? Ну а на мэйнфреймах z/Architecture это делается одной командой. А если необходима десятичная арифметика? (а она необходима для финансового сектора -- недаром соответствующие вещи есть, например, в Жабе) На большинстве архитектур, включая IA-32, будут пляски с бубном, чтобы добиться того же результата, используя целочисленную двоичную арифметику, ну а мэйнфреймы ещё с Системы 360 имеют и команды десятичной арифметики. Да, и операция пересылки строк на них тоже используется весьма и весьма часто -- пересылать ведь не только строки в узком смысле приходится, а произвольные блоки данных.

(Замечу, что есть по крайней мере одно типа исследование, которое "доказывает", что плотность кода CISC ниже, чем RISC: но, во-первых, сами авторы там между строк замечают, что, фактически, их сравнение неправомерно, поскольку сравнивают современные 64-разрядные архитектуры ARMv8 и RISC-V с древними CISCами, где 64-разрядные расширения были добавлены много позже -- а соответственно, все короткие коды операций уже попросту были заняты; а во-вторых, они начисто игнорируют случаи, когда сложные операции выполняются на CISCах одной-двумя командами, а на RISCах требуют подпрограмм значительного размера -- т.е., попросту говоря, подбирают тесты под требуемые результаты)

Усложнение декодера -- тоже далеко не факт. В частности, декодер для системы команд Thumb-2 будет сложней, чем даже для современных мэйнфреймов z/Architecture, не говоря о классической Системе 360

Усложнение управляющей логики, о чём автор почему-то не говорит, -- чуть ли не единственный пункт, где можно было бы согласиться, но и здесь не всё просто. Многие CISCи (а с 1970-х годов -- пожалуй, все или почти все) используют не чисто схемное, а микропрограммное или смешанное микропрограммно-аппаратное управление, что многократно упрощает конструкцию управляющей части процессора (память микропрограмм многократно проще и дешевле, чем управляющая логика). Впервые микропрограммы были использованы на младших и средних моделях Системы 360; старшие модели делались со схемным управлением (и были конвейерными) -- разработчики опасались, что микропрограммное управление будет медленнее. Но, как оказалось, оно может быть столь же быстрым: это зависит от особенностей микроархитектуры; поэтому, набив руку, его стали использовать и в топовых моделях. Скажем, в младших моделях (IBM System360/30 и наша ЕС-1020) АЛУ было 8-разрядным, а соответственно, простейшая 32-разрядная операция требовала для своего выполнения четыре микрокоманды только для собственно вычислений -- не говоря о всяких других потребных действиях; понятно, что это было очень медленно -- но требовало очень мало аппаратуры, почему, собственно, и использовалось. Однако, если не экономить оборудование для выборки команд и для собственно выполнения операций, разницы в скорости с машиной с чисто схемным управлением не будет, а разница в объёме аппаратуры окажется весьма значительной. Скажем, наша ЕС-1130 при заполненном конвейере выполняла простые команды регистр-регистр и регистр-память (!) за один такт, но была микропрограммной, просто вся микропрограмма для этих простых команд состояла из единственной микрокоманды (а вот выборка команды, декодирование, вычисление адресов операндов и их подготовка в ней были сделаны чисто аппаратно; микропрограмма управляла лишь ходом выполнения собственно обработки данных -- была, как я уже сказал, элементарнейшей для простейших операций, но сложной для сложных). Примерно ту же идею, только в значительно более сложном исполнении (в первую очередь, из-за суперскалярности и внеочередного исполнения) используют и современные CISC-процессоры (что у интеловских процессоров есть микрокод, наверное, все знают).

В 1980-х RISC-микропроцессоры (подчёркиваю микро) показывали более высокую производительность над CISC-микропроцессорами за счёт того, что благодаря очень малому числу команд (первый из них насчитывал, насколько помню, всего 31 команду) и другим упрощениям его можно было сделать конвейерным, но всё равно впихнуть на один кристалл; для CISC-процессора на тот момент это было невозможно, поэтому приходилось выбирать: либо медленная микропрограммная реализация на одном кристалле, либо быстрая, но на куче микросхем (в IA-32 первым конвейерным стал 80486; в мэйнфреймах конвейерными давно уже были все модели, а некоторые -- и суперскалярными, но они достаточно долго оставались многокристальными). Однако, если рассматривать все процессоры, а не только микро, RISC по скорости первыми отнюдь не были -- но далеко не каждый может позволить себе поставить мэйнфрейм или, тем более, какой-нибудь Cray с охлаждением жидким азотом (и хорошо, если азотом). Но в 1990-х, когда количество транзисторов на кристалле значительно выросло, конвейерные CISC тоже стали однокристальными -- и догнали (а нередко и перегнали) по скорости RISC. Да и сами современные RISC, прямо скажем, зачастую мало чем отличаются от CISC: имеют сотни, а то и тысячи команд, включают ряд достаточно сложных операций и т.д. Изначальная идея RISC (напомню, у первого из них была 31 команда, и среди них не было даже умножения и деления -- они сложные) оказалась несовместима с высокой производительностью, поэтому от неё отошли; фактически единственное, что осталось -- это отсутствие команд обработки данных вида "регистр-память" или "память-память" -- вся обработка выполняется исключительно в регистрах.

Более того, и конвейер, и суперскалярность появились ещё на машинах 1960-х годов. У нас первой конвейерной машиной была БЭСМ-6 (1967 год), у американцев всё это появилось ещё раньше.

А сколько нафиг пошло систем 60-70-х годов...

Проблема в том, что высокой производительности не добиться, если игнорировать технические аспекты. Правда, требовать от художников понимания, как работает железо, -- это перебор, но в компаниях-разработчиках должны быть люди, которые это понимают и при необходимости могут оптимизировать, что там художники наваяли, грубо говоря.

Вершинный или вертексный нормальный рабочий сленг.

Сленг годится для устного общения между коллегами, в статьях же лучше придерживаться нормального литературного языка. Впрочем, Ваше право как автора выбирать те выражения, какие Вам нравятся. Но тогда непонятно, почему в одном месте Вы пишете "вершинный буфер", а в другом -- "вертексный буффер".

Ну а ошибки в русском языке в любом случае имеют место быть, и они никакой текст не красят.

Вертексный буффер

А всегда писать нормальным русским языком -- "вершинный буфер" -- никак нельзя? Вообще, грамотность речи где-то в районе двойки, да и с технической точки зрения определённые недостатки есть. Например:

Сначала CPU вызывает API ((Direct3D/Vulkan/OpenGL/Metal). Дальше драйвер GPU конвертирует API вызовы в машинные команды и формирует Command Buffer. После происходит копирование данных из системной памяти в VRAM с помощью шины PCIe.

Во-первых, графический API вызывает не "CPU", т. е. центральный процессор, а программа, желающая что-либо вывести посредством этого API. Вот выполняется она на центральном процессоре, это да -- но вызывает она, а не процессор, он просто выполняет команду за командой, и вызов функций API для него ничем не отличается от любой другой работы.

Драйвер (пусть будет так, хотя там целый комплекс программ, и драйвер собственно железки -- лишь одна из частей) тоже работает на центральном процессоре, но формирует не только "машинные команды" (причём графического процессора), но и кучу дополнительных вещей, необходимых для установки состояния графического конвейера и т.д. и т.п.

Наконец, копирование из системной памяти в видеопамять имеет место не всегда (иногда видеопамяти не хватает, и в неё загружаются лишь наиболее критические вещи, а остальное по мере необходимости берётся прямо из системной памяти, а иногда -- например, на ноутбуках со слабой графикой -- видеопамяти как таковой просто нет), да и к типу шины оно ни разу не привязано. В частности, графический процессор, расположенный на одном кристалле с центральными процессорами архитектуры ARM, почти наверняка будет связан шиной AXI -- но это играет роль лишь для разработчиков железа и, в какой-то мере, для разработчиков операционных систем и драйверов (какие механизмы PnP используются и т.д.), но не для собственно "графических" программистов.

Не обязательно от кластера №2, положение корневого каталога (номер его первого кластера) в FAT32 указывается полем BPB_RootClus.

Вообще, процесс загрузки сильно сложней -- в частности, она может производиться не только с дисков.

в последних двух байтах первого сектора которого находится специальная запись двухбайтовая 0x550xAA, указывающая, что он содержит загрузчик ядра. Обычно сектор равняется 512 байт, но на некоторых устройствах оооочень редко может быть другое количество байт на сектор

Угу, размер сектора не обязательно равен 512 байтам. А вот насчёт сигнатуры 55AA сказано неверно: она находится не в последних двух байтах первого сектора, а в байтах по смещениям 510 и 511 от начала первого сектора. Т.е. если сектор имеет размер 4 килобайта, весь загрузчик, включая эту сигнатуру, будет занимать его первые 512 байт, а остальные 3,5 килобайта игнорируются.

BootLoader должен после своего запуска найти на диске ядро системы, загрузить его с диска в оперативную память, перейти из реального режима (x16) в защищённый режим (x32) и передать управление ядру

Вообще, в общем случае, первичный загрузчик (тот, что загружается кодом BIOS) ищет и загружает вторичный загрузчик, а уже вторичный что-то там делает для загрузки ОС.

Регистр начала стека sp устанавливаем 0x7Bff - ближайшая пустая область в оперативной памяти. А регистр конца стека ss обнуляем (стек растёт вниз).

Насчёт выравнивания стека уже сказали (хотя я, честно говоря, не помню, является ли это требованием для IA-32 aka x86). А вот SS -- это не регистр конца стека, это регистр сегмента стека. Его содержимое совместно с SP и формирует физический адрес памяти, где находится текущая вершина стека.

В современном мире используется LBA запись, в то время, как биос для считывания использует систему CHS.

Вообще-то, BIOS, кроме самых ранних версий, успешно работает и с LBA, только номера функций другие. И они (эти функции), кажется, не умеют работать с дискетами -- только с жёсткими дисками.

Корневой каталог расположен после загрузочного сектора и таблиц FAT.

В FAT12 и FAT16 -- да, в FAT32 -- нет.

Отнимаем от si 3 - первые две записи в FAT заняты корневым каталогом и меткой тома

Неверно. Нулевой элемент FAT содержит в своём первом байте значение, хранящееся в поле BPB_Media; остальные его биты всегда установлены.

Первый элемент FAT12 всегда содержит значение, отмечающее конец цепочки кластеров файла. В FAT16 и FAT32 изначально это тоже признак конца цепочки кластеров, однако биты 15:14 в FAT16 и 27:26 в FAT32 имеют специальное назначение:

  • 15/27 — если установлен, том является «чистым»; если сброшен, том является «грязным», т. е. он не был должным образом демонтирован драйвером перед завершением его использования, а соответственно, информация на нём может быть повреждена;

  • 14/26 — если установлен, ошибок ввода-вывода при работе с томом не возникало; если сброшен, обращение к некоторым секторам раздела вызывало ошибки ввода-вывода.

Небольшая придирка:

(новое поколение мейнфреймов IBM Z/S390)

Новое поколение z/Architecture; того, что Вы написали, в природе не существует, ну а архитектура ESA/390 (временами называемая S/390) закончилась в 2000-м году, будучи сменённой как раз z/Architecture (последняя -- 64-разрядная, тогда как все предыдущие архитектуры, начиная с Системы 360, -- 32-разрядные, но совместимость на уровне прикладного кода сохраняется).

Управляющая логика тоже далеко не всегда требует дублирования. Скажем, на основе кода команды вырабатывается энное количество управляющих сигналов плюс 1-2 контрольных -- а дальше это где-то проверяется на чётность.

С обнаружением проблем в суперскалярном проце тоже особых проблем нет: чем занимается сдохший узел в каждый момент времени, известно. Есно, усложнение будет, но отнюдь не в 100500 раз (а по площади современных кристаллов -- так вообще мизер, ведь там бОльшая часть площади -- это разного рода кэши и буферы, которые контролируются элементарно).

Я написал про регистры вообще, а не конкретно про регистры общего назначения. Очевидно, что к числу регистров PSW тоже относится -- как относятся и регистры с плавающей запятой, и появившиеся в Системе 370 управляющие регистры, регистр префикса, регистры средств отсчёта времени, и т.д. и т.п.

Зачастую достаточно обнаружить сбой, схем для чего требуется не очень много по объёму (реальное дублирование требуется для АЛУ, но их площадь на современных кристаллах незначительна -- это не машины 1960-х, где АЛУ могло по объёму аппаратуры занимать чуть ли не половину всего процессора). При обнаружении сбоя происходит прерывание от схем контроля (имелось ещё в Системе 360 -- собственно, все механизмы уже тогда были заложены, затем лишь шло их совершенствование), при котором в память записывается, в т.ч., содержимое всех программно доступных регистров процессора вместе с индикацией достоверности их содержимого. В многопроцессорной системе (а современные, понятное дело, все такие) сигнал сбоя одного процессора может передаваться другим процессорам, что при соответствующей программной поддержке позволяет "подхватить" задачу, выполнявшуюся на вышедшем из строя процессоре.

В общем, достаточно обеспечить высокую (желательно 100%) вероятность обнаружения сбоя и сохранность исходного содержимого регистров -- тогда продолжение работы на другом процессоре обеспечивается без проблем.

Плюс, ещё в OS/360 был механизм контрольных точек, позволяющий при нужде перезапустить приложение не с самого начала, а с какого-то промежуточного момента -- помогает, если продолжить выполнение прямо с точки сбоя невозможно (например, утрачено достоверное содержимое регистров).

Замечу, что в ассемблере для Системы 360 (а её продажи начались в 1965-м) был COPY = INCLUDE и CSECT = SEGMENT, если сравнивать с интеловско/мелкомягким синтаксисом. Вот PROC не было, да -- и он не нужен для ассемблера от слова "вообще". Ассемблерный текст должен 1:1 соответствовать машинным командам, которые будут сгенерированы, а соответственно, никаких автоматических прологов-эпилогов функций быть не должно (тем более, если говорить про общий случай, то в ассемблерных подпрограммах прологи-эпилоги часто могут быть "нестандартными", у одной подпрограммы может быть несколько точек входа и т.д. и т.п.). Если нужно автоматизировать повторяющиеся последовательности кода -- для этого есть макрокоманды.

Нет, чтоб увидеть, как код на исходном языке, будь то ассемблер, С/С++ или иной традиционно компилируемый, превращается в выполнимую программу (в ехешник, грубо говоря). Чтобы пришло понимание, что просто перевести мнемоники в единички-нолики мало, нужно (в общем случае) связать кучу модулей вместе, назначив секциям и их частям адреса, какую роль играет скрипт компоновщика, ну и всё такое. Без этого нет полного понимания, как программа готовится к выполнению.

Система сборки же -- это отдельный вопрос, на первых порах она не нужна, достаточно ручками запускать транслятор и компоновщик.

Как по мне, при изучении ассемблера на первых порах надо именно что вручную вызывать и транслятор ассемблера, и компоновщик: чтоб понять реальную последовательность действий и что за что отвечает. И лишь когда поймёшь, как несколько объектников связываются в выполняемую программу (с возможным добавлением библиотечных модулей), тогда можно считать, что более-менее понял, как это происходит (а как в действительности работает -- это уже надо вникать в форматы объектных и выполняемых файлов, тонкости работы компоновщика и др., что явно не для новичка).

Information

Rating
1,786-th
Location
Солнечногорск, Москва и Московская обл., Россия
Date of birth
Registered
Activity

Specialization

Embedded Software Engineer
Lead