Общая информация о языке Assembler
Эта Лекция знакомит слушателя с основными знаниями в области архитектуры RICS-V и языка Assembler. Если говорить вкратце, она охватывает демонстрацию способов применения языка Assembler для программирования под архитектуру RISC-V и в целом понимание того, почему знание языка Assembler является преимуществом для любого разработчика программного обеспечения.
После освоения материала лекции вы должны будете:
- понимать связь между языком Assembler и микропроцессором RISC-V;
- понимать особенности языка Assembler;
- понимать преимущества от знания языка Assembler;
- понимать взаимосвязь между микропроцессорами, языком Assembler и архитектурой RISC.
Общая информация о языке Assembler
RISC-V
RISC-V - это спецификация архитектуры и набора инструкций для 32, 64 и 128 битных микропроцессоров (ISA, Instruction Set Architecture). ISA обеспечивает основу для языка машинных кодов микропроцессоров. Она описывает стандарт, которому должен соответствовать микропроцессорRISC-V.
Термин RISC расшифровывается как Reduced Instruction Set Computer, компьютер с ограниченным набором инструкций, а используемый суффикс "V" соответствует римской цифре 5, которая показывает версию RISC-архитектуры, разрабатываемой университетом Беркли, Калифорния.
Спецификации архитектуры RISC-V опубликованы под лицензией "Creative Commons", таким образом, RISC-V является открытой архитектурой и набором инструкций, что позволяет любому желающему проектировать процессоры на этой архитектуре без каких-либо отчислений на использование прав собственности.
Руководство с описанием набора инструкций для архитектуры RISC-V разбито на два тома. Первый том, описывающий инструкции пользовательского уровня, содержит основную информацию об архитектуре, базовый набор инструкций для 32-х, 64-х и 128-ми битных целочисленных архитектур, стандартных расширениях базового набора инструкций и основные соглашения. Говоря в общем, первый том содержит всю необходимую информацию для того, чтобы можно было писать программы на языке Assembler, поскольку большинство пользовательских программ на языке Assembler задействуют именно инструкции пользовательского уровня. Второй том руководства содержит описание архитектуры привилегированных инструкций, которые используются в основном при разработке операционных систем и низкоуровневых решений для встраиваемых систем.
В рамках этого курса будут рассмотрена информация лишь из первого тома.
Язык Assembler
Языки программирования Assembler - группа языков программирования, которые позволяют представить в удобном для чтения и написания человеком представление машинного кода. Инструкции, написанные на этих языках, могут быть напрямую транслированы в машинные коды. Языки Assembler - языки программирования самого низкого уровня и различаются в зависимости от того, под какую архитектуру микропроцессора они используются. Таким образом говорят, что язык Assembler - это аппаратно-зависимый язык.
Программы на языке Assembler пишут с использованием простых текстовых редакторов или специальных редакторов кода - интегрированных сред разработки. Сборка программ с использованием утилит, называемых ассемблерами (сборщиками), генерирует исполняемый машинный код.
Поскольку есть прямое соответствие между набором инструкций в машинных кодах и инструкциями на языке Assembler, существует процесс дизассемблирования (Disassembly), который является логически обратным процессу сборки исполняемых файлов из исходного кода и позволяет получить, наоборот, исходный код на языке Assembler из скомпилированного исполняемого бинарного кода. При выполнении этой процедуры можно получить практически полный код на языке Assembler за исключением некоторых объектов исходного кода, которые теряются в процессе компиляции, например, имена меток, имена переменных, комментарии исходного кода и т.д.
Знание языка Assembler и способов его применения для решения практических задач помогает понять взаимодействие между программным и аппаратным обеспечением. В частности, эти знания принесут пользу в решении таких задач, как проектирование компьютерной архитектуры, аппаратное программирование и близкое к аппаратному, отладка программного обеспечения, обратный инжиниринг (reverse engineering) и разработка компиляторов.
Обычно части встраиваемых систем и операционные системы реализуются на языке Assembler, поскольку имеют самое близкое отношение к аппаратной составляющей: загрузчики, обработчики прерываний, драйвера устройств и различные процедуры и функции для управления ресурсами процессора.
Отладка программ, то есть поиск и устранение неисправностей в программном обеспечении, зачастую предполагает пошаговое исполнение программного кода с анализом работы машинных инструкций как раз на уровне инструкций языка Assembler. Решение задач обратного проектирования (reverse engineering), в ходе которых программист анализирует алгоритм работы скомпилированного исполняемого кода, может быть выполнено лишь при понимании принципа исполнения машинных инструкций, и, следовательно, знания языка Assembler.
Также знания языка Assembler требует разработка компиляторов и анализ промежуточных данных, формируемых компилятором. Программы, написанные на высокоуровневых языках программирования, таких как С++ или Rust, транслируются в промежуточное представление на языке Assembler в процессе компиляции.
Краткие сведения о микропроцессоре
Для достижения целей курса мы рассмотрим микропроцессор лишь поверхностно. Микропроцессор имеет модуль управления (control unit), арифметико-логическое устройство, АЛУ (arithmetic logic unit, ALU), регистры (registers) и шины данных и команд (signal/data buses) для обмена данными между микропроцессором и внешней памятью.
Микропроцессор выполняет инструкции с применением модуля управления. Любая компьютерная программа - это набор указаний микропроцессору о том, что нужно делать и с чем. Основная задача модуля управления заключается в трансляции инструкций микропроцессору и контроле за ходом исполнения программ.
Исходя из названия, арифметико-логическое устройство (АЛУ) позволяет выполнять целочисленные логические и арифметические операции, при этом логические операции - это побитовые операции над целыми числами.
Регистры процессора - небольшие именованные ячейки памяти, расположенные прямо внутри микропроцессора. Микропроцессор умеет быстро загружать данные в регистры из внешней памяти или получая их от других устройств, обрабатывать данными внутри регистров, а также записывать данные из своих регистров во внешнюю память или передавать на внешние устройства.
Компьютерная программа размещается в памяти. Области памяти, куда могут быть загружены как код, так и данные, имеют адреса - целочисленные значения, отождествляемые с номерами ячеек памяти (обычно однобайтными). Специальный регистр, называемый "программный счётчик", хранит адрес инструкции, которая будет выполнена следующей. Значением этого регистра пользуется модуль управления микропроцессора.
Как будет упомянуто далее, доступ к аппаратным устройствам обычно также реализуется по адресам.
Классический RISC-конвейер
Обычно набор инструкций микропроцессоров относят к RISC (Reduced Instruction Set Computer, компьютер с ограниченным набором инструкций) или CISC (Complex Instruction Set Computer, компьютер со сложным набором инструкций). Разделение очень простое: в RISC-архитектуре есть меньшее, чем в CISC, число инструкций и часто некоторые простые действия приходится описывать несколькими RISC-командами, в то время как в CISC-архитектуре может быть всего одна команда, реализующая требуемое действие. Но, при этом, для RISC-архитектуры характерно фиксированное время исполнения каждой команды в тактах и фиксированный размер каждой команды, чего нельзя сказать о командах CISC-архитектуры.
Для обработки какой-либо инструкции, типичный микропроцессор с RISC-архитектурой выполнит конвейер операций, состоящий из пяти стадий:
- извлечение инструкции, instruction fetch (IF)
- декодирование инструкции, instruction decode (ID)
- выполнение инструкции, instruction execute (EX)
- доступ к памяти, memory access (MEM)
- запись результата, write back (WB)
Машинная инструкция извлекается из области памяти, на которую ссылается регистр - счётчик инструкций. В ходе декодирования инструкции микропроцессор понимает, что он должен сделать и какие ещё данные ему необходимо извлечь для выполнение операции. Затем инструкция выполняется, чаще всего при этом задействуется блок АЛУ. Если инструкция подразумевает работу с памятью, выполняется обращение к памяти. И затем, если после выполнения инструкции формируется результат, он записывается в один из регистров.
Этот конвейер (англ. pipeline) может быть выполнен как единой последовательностью для какой-либо инструкции, так и конвейеризован. Конвейеризация - процесс, в ходе выполнения которого происходит разделение выполнения инструкции на последовательно выполняемые шаги, за счёт чего можно достичь распараллеливания выполнения этапов обработки: выполнив для первой команды стадию IF, её результат передаётся для дальнейшего выполнения на стадию ID, и микропроцессор может сразу же начать выполнение второй инструкции (снова выполнить её извлечение из памяти).
Контрольные вопросы
- Что такое набор инструкций (instruction set)?
- Чем отличаются архитектуры CISC и RISC?
- Почему язык ассемблера может отличаться для различных микропроцессоров?
- Как называется самая быстрая память внутри микропроцессора?
- Какие основные этапы можно выделить у конвейера типичного процессора с RISC-архитектурой?
- Что называется конфликтом конвейера?