Понимание взаимодействия между регистрами и I/o на карте памяти
Основы процессорно-периферийной коммуникации
Для программиста, пишущего высокоуровневый код, чтение с адреса памяти или запись на переменную является простой, атомарной операцией. Однако эта простота маскирует сложный аппаратный механизм, который является фундаментальным для всех современных вычислений. Способность процессора управлять жестким диском, отображать изображение на экране или получать пакет с сетевой карты полностью зависит от сложной взаимосвязи между двумя основными концепциями: CPU регистры и . ММИО обеспечивает высокоскоростное временное хранилище, необходимое для вычислений, в то время как MMIO действует как мост, отображая интерфейсы управления периферийными устройствами в стандартное адресное пространство памяти системы. Понимание взаимодействия между этими двумя элементами имеет важное значение для системных программистов, встроенных инженеров и всех, кто стремится оптимизировать производительность на границе аппаратно-программного обеспечения.
Микроархитектура регистров ЦП
Регистры представляют собой самый высокий уровень памяти в иерархии хранения, расположенный непосредственно в ядре ЦП. Они представляют собой рабочую память процессора, держащую немедленные данные, адреса и управляющую информацию, необходимую для выполнения инструкций. В отличие от более медленных типов памяти, таких как DRAM или NAND flash, регистры строятся с использованием быстрых статических ячеек RAM (SRAM) или флип-флопов и предназначены для одноциклового доступа на тактовой частоте ЦП.
Регистры общего назначения и пути исполнения
Наиболее заметным набором регистров для ассемблера являются Регистры общего назначения (GPRs). В архитектурах, таких как ARM64 (AArch64), программист имеет доступ к 31 регистрам общего назначения (X0-X30). Эти регистры являются эксклюзивным источником и назначением операций арифметического логического блока (ALU). Когда процессор выполняет такую инструкцию, как , значения, хранящиеся в физических регистрах X1 и X2, считываются, передаются ALU, и результат записывается обратно в регистр X0. Эта тесная связь между регистрами и исполнительными блоками определяет архитектуру хранилища нагрузки , распространенную в современных процессорах. Данные должны загружаться из памяти в регистр, прежде чем им можно манипулировать, и результат должен храниться обратно. Это основной путь, по которому к регистрам MMIO обращаются.
Регистры специальных целей и системный контроль
Помимо GPR, процессор полагается на набор регистров специального назначения для поддержания состояния и управления потоком выполнения. Они часто невидимы для кода уровня приложения, но постоянно доступны для операционной системы и прошивки.
- Программный счетчик (PC): Этот регистр содержит адрес памяти исполняемой в настоящее время инструкции. Он автоматически увеличивается и может быть изменен инструкциями ветви.
- Указатель стека (SP): Используется для управления стеком вызовов, содержащим адрес верхней части текущего кадра стека. Он имеет решающее значение для вызовов функций и локального хранилища переменных.
- Регистр состояния (PSR/FLAGS): Хранит коды условий, возникающие в результате операций ALU, такие как ноль, перенос, переполнение и отрицательное. Эти флаги контролируют условные инструкции ветвей и необходимы для реализации циклов и заявлений.
- Link Register (LR) / X30: В архитектурах ARM этот специальный GPR содержит обратный адрес для вызова функции, что позволяет эффективно выполнять вызов подпрограммы без необходимости немедленного нажатия обратного адреса на стек.
Современное управление реестром: переименование и спекуляция
В современных внепорядковых процессорах физический регистровый файл значительно больше, чем набор архитектурных регистров, заданный архитектурой набора инструкций (ISA). ЦП использует метод, называемый переименование регистра , для преодоления опасностей данных. Например, если компилятор повторно использует архитектурный регистр X0 для множества несвязанных значений, аппаратное обеспечение будет отображать эти логические ссылки на уникальные физические регистры. Это позволяет ЦП выполнять инструкции параллельно, даже когда ISA предполагает последовательную зависимость. Хотя эта сложность прозрачна для программного обеспечения, она подчеркивает критическую роль регистров в поддержании высокой пропускной способности команд, и это напрямую влияет на задержку доступа MMIO при побочных эффектах.
Основы I/O (MMIO) на карте памяти
I/O с картой памяти - это аппаратная методология, в которой регистрам управления и данных периферийных устройств присваиваются адреса в стандартной карте памяти процессора. Вместо использования выделенных инструкций ввода/вывода (как в архитектуре x86 IN/OUT), MMIO позволяет процессору взаимодействовать с устройствами с использованием стандартных инструкций загрузки (LDR) и хранения (STR).
Архитектура единого адресного пространства
В системе MMIO физическое адресное пространство делится между системной ОЗУ, ROM/flash и периферийными регистрами. Для регистров устройств зарезервирован определенный диапазон адресов. Когда процессор выдает команду загрузки или хранения на адрес в пределах этого зарезервированного диапазона, шина памяти декодирует адрес и направляет транзакцию на соответствующую периферийную шину (например, AMBA AXI или PCI Express), а не на контроллер памяти.
Например, на типичном микроконтроллере ARM Cortex-M карта памяти строго соблюдается: адреса от до предназначены для кода, до для SRAM, а до зарезервированы исключительно для периферийных устройств. Это разделение позволяет ткани шины определять строгие разрешения доступа и временные характеристики для каждой области.
Сравнение MMIO и Port-Mapped I/O (PMIO)
Альтернативой MMIO является изолированный I/O или порт-картированный I/O (PMIO), исторически используемый архитектурой x86. PMIO использует выделенные аппаратные штифты и специальные инструкции (IN/OUT) для доступа к отдельному адресному пространству ввода/вывода. Оба метода имеют различные конструктивные компромиссы.
- Преимущества MMIO: Он повторно использует всю мощность набора команд памяти процессора. Вы можете использовать дереференцию указателей в C/C++, применять операции битового поля и использовать все режимы адресации. MMIO не требует специальных механизмов защиты ввода/вывода за пределами существующего блока управления памятью (MMU).
- Преимущества PMIO: Он не потребляет ценное адресное пространство с карты памяти. Выделенное пространство ввода/вывода обеспечивает чистое разделение между памятью данных и регистрами управления, что может упростить аппаратный дизайн в некоторых устаревших системах. Однако, порт на основе ввода/вывода, как правило, имеет более низкую пропускную способность и не может быть использован для передачи вспышек памяти.
Современные системы x86 по-прежнему используют PMIO для унаследованной совместимости устройств (например, унаследованный контроллер клавиатуры PS/2), но высокопроизводительные устройства, такие как GPU и NVMe SSD, полагаются исключительно на MMIO через шину PCI Express.
Доступ к регистрам устройств из кода высокого уровня
При написании драйверов устройств на C или C++ доступ к регистрам MMIO требует определенной семантики, чтобы предотвратить оптимизацию компилятора от нарушения взаимодействия с аппаратным обеспечением. Ключевое слово имеет важное значение. Оно сообщает компилятору, что значение по адресу может изменяться без ведома компилятора (например, из-за изменений состояния аппаратного обеспечения) и что записи на адрес не должны быть оптимизированы. Например: . Без квалификатора компилятор может оптимизировать опрос по петле для бита состояния в одно чтение, в результате чего программное обеспечение зависает.
Слияние регистров и MMIO
Истинное «взаимодействие» происходит каждый раз, когда программе необходимо отправить данные на периферийное устройство или получить от него данные. Процесс включает в себя перемещение данных между регистрами процессора и регистрами устройств MMIO с использованием инструкций загрузки / хранения, но аппаратные и протокольные последствия уникальны.
Семантика загрузки/хранилища памяти устройства
Выполнение команды, подобной , где X1 содержит адрес регистра данных передачи устройства, запускает определенную последовательность. Значение из регистра общего назначения ЦПУ (W0) помещается на шину данных. Ткань шины декодирует целевой адрес и идентифицирует его как область MMIO. Важно, что тип памяти для этой области установлен на Strongly Ordered или Устройство Память. Это предотвращает ЦПУ и шину от выполнения спекулятивных считываний, записи, объединения или чтения, сливающихся с этими адресами, поскольку такие оптимизации могут вызвать побочные эффекты, такие как инициирование сброса устройства дважды или чтение устаревших данных.
Опросы, прерывания и регистрация флагов статуса
Наиболее простой шаблон взаимодействия — опрос. ЦПУ считывает регистр состояния устройства (адрес MMIO) в GPR, проверяет конкретный бит с помощью битмаски и зацикливается до тех пор, пока аппаратное обеспечение не установит флаг, указывающий на данные, готов. Это простейшая форма взаимодействия, но растрачивает циклы ЦП. Модель, управляемая прерыванием, намного эффективнее. Когда устройство имеет данные, оно утверждает линию прерывания. ЦПУ приостанавливает свой текущий поток, считывает регистр состояния прерывания (другой адрес MMIO) для поиска источника прерывания, а затем считывает регистр данных. Это включает сохранение и восстановление регистров ЦП в стек, выделяя накладные расходы на переключение контекста.
Прямой доступ к памяти (DMA) и координация регистров
Для устройств с высокой пропускной способностью, таких как сетевые карты или контроллеры хранения, перемещение данных через GPRs ЦП чрезмерно медленно. Direct Memory Access (DMA) решает эту проблему, позволяя периферийному устройству передавать данные непосредственно в системную ОЗУ без привлечения ЦПУ для каждого слова. Однако DMA по-прежнему в значительной степени полагается на регистры ЦП и MMIO для своей конфигурации.
- Процессор записывает в регистры MMIO контроллера DMA, чтобы установить адрес источника, адрес назначения и длину передачи.
- Процессор пишет в отдельный регистр MMIO «старт», чтобы начать передачу.
- Пока DMA находится в процессе разработки, процессор может свободно выполнять другой код.
- После завершения передачи контроллер DMA записывает в свои собственные регистры состояния и вызывает прерывание. Затем процессор должен прочитать эти регистры, чтобы убедиться, что транзакция была успешной.
Это рукопожатие, в котором регистры процессоров настраивают систему, двигатели DMA выполняют тяжелую подъемную силу, а MMIO регистрирует состояние отчета, является основой высокопроизводительного ввода / вывода.
Расширенные аспекты современных реализаций
По мере роста сложности системы появилось несколько сложных функций для управления особенностями MMIO и его взаимодействия с состоянием регистра процессора.
Кэширование и согласованность памяти
Регистры устройств по своей сути неидемпотентны; чтение регистра может очистить флаг прерывания, а запись регистра может запустить двигатель. Поэтому области MMIO почти всегда помечены как Uncacheable или Устройство-nGnRnE в таблицах страниц MMU. Это заставляет каждую нагрузку или хранилище напрямую обращаться к шине, минуя кэши L1 и L2. Это вводит задержку, поскольку удар кэша по устаревшему значению регистра устройства может быть катастрофическим. Барьеры памяти (например, DSB ARM или MFENCE x86) часто необходимы после того, как последовательность записей MMIO достигает устройства до выполнения последующей инструкции, особенно при работе со слабо упорядоченными моделями памяти.
MMIO в PCI Express
Стандарт PCI Express (PCIe) является доминирующим высокоскоростным межсоединением в ПК, серверах и встроенных системах. PCIe устройства выставляют свои регистры управления через MMIO. Во время загрузки системы прошивка или ОС сканирует шину PCIe и присваивает области памяти регистрам базовых адресов каждого устройства (BARs). BAR определяют размер и тип окна MMIO, которое требуется устройству. Когда процессор записывает на адрес в BAR устройства, корневой комплекс PCIe преобразует транзакцию памяти в пакет уровня транзакции PCIe (TLP) и направляет его к конечной точке. Это позволяет обеспечить чрезвычайно высокую пропускную способность и доступ к регистрам устройств, памяти и пространствам конфигурации с низкой задержкой.
Проблемы виртуализации и IOMMU
В виртуализированных средах гостевой операционной системе нельзя предоставить прямой физический доступ к регистрам MMIO устройства, так как это нарушит изоляцию. Гипервизор должен захватывать и эмулировать гостевые доступы MMIO, что медленно. Современное оборудование решает эту задачу с помощью блока управления памятью ввода-вывода (IOMMU) . IOMMU находится между устройством и шиной памяти. Он позволяет гостевой ОС напрямую управлять устройством, отображая MMIO-регистры устройства в виртуальное адресное пространство гостя и переводя физические адреса гостя в реальные адреса машины для DMA. Эта проходящая модель обходит гипервизор, давая почти нативную производительность при сохранении изоляции. Взаимодействие теперь включает в себя настройку ЦПУ таблиц страниц для IOMMU, задача, которая требует точного доступа MMIO к собственным регистрам управления IOMMU.
Практическое применение регистров и MMIO
Понимание этих концепций не просто академическое. На этой основе построен каждый периферийный драйвер, написанный для Linux, Windows или RTOS.
Универсальный асинхронный приемник/передатчик (UART)
UART — это простое периферийное устройство, которое иллюстрирует взаимодействие. Для передачи персонажа водитель должен опрашивать регистр состояния (MMIO), чтобы проверить, пуст ли регистр удержания передачи (THR). Он считывает это значение в GPR, тестирует бит и зацикливается. Как только THR пуст, водитель записывает символ на адрес THR (MMIO). Эта инструкция единого хранилища перемещает данные из GPR на шину и в регистр сдвига UART. На стороне приема, когда персонаж прибывает, UART устанавливает бит в свой регистр состояния. Водитель считывает этот регистр, видит готовый флаг данных и считывает буферный регистр приема, чтобы получить байт в GPR.
Графические процессоры (GPU)
GPU — это массивно параллельные процессоры, которые в значительной степени полагаются на MMIO. ЦП взаимодействует с драйвером GPU через буфер командных колец и набор регистров MMIO. Чтобы представить работу, ЦП записывает команды в кольцевой буфер в системной памяти. Затем он записывает в конкретный регистр MMIO «дверного звонка» на GPU. Этот регистр дверного звонка сигнализирует GPU о том, что новые команды ждут. Затем GPU считывает кольцевой буфер через DMA. Все взаимодействие организуется процессором, записывающим в память и один регистр MMIO, используя адресное пространство для инициирования сложной параллельной обработки.
Контроллеры сетевого интерфейса (NIC)
Современные NIC используют аналогичный принцип. У них есть набор регистров MMIO для управления и набор дескрипторных колец в основной памяти. Когда пакет поступает, NIC использует DMA для размещения пакетных данных в предварительно выделенный буфер памяти и записывает буферный дескриптор в кольцо. Затем он обновляет собственный регистр MMIO «хвостовой указатель» и опционально поднимает прерывание. Драйвер ЦП использует MMIO считывания для проверки обновленных хвостовых указателей и определяет, какие буферы обрабатывать. Эффективность этого процесса полностью зависит от способности ЦП выполнять когерентные нагрузки и хранит в пространстве MMIO.
Оценка ввода/вывода с помощью карты памяти
В то время как MMIO является доминирующей парадигмой для современной периферии, это компромисс с различными плюсами и минусами.
Преимущества в системном дизайне
MMIO упрощает модель программирования, устраняя отдельный набор команд ввода/вывода. Он позволяет легко интегрироваться с блоком защиты памяти процессора (MMU) и позволяет использовать стандартный код C для разработки драйверов. Он хорошо масштабируется до высокопроизводительных устройств, так как DMA может быть легко скоординирована через дескрипторные кольца на основе памяти. Унифицированное адресное пространство позволяет эффективно передавать всплески и хорошо согласуется с современными архитектурами шины, такими как AXI и PCIe.
Потенциальные недостатки и подводные камни
Одним из существенных недостатков является потребление физического адресного пространства. На 32-битных системах выделение большого диапазона адресного пространства неиспользуемым или медленным устройствам может фрагментировать карту памяти. Кроме того, доступ MMIO по своей сути является последовательным и некэшируемым, что делает его медленнее, чем доступ к кэшированным данным. Распространенной ошибкой в разработке драйверов является отсутствие ключевого слова , что приводит к оптимизации компилятора, чтобы нарушить доступ к аппаратному обеспечению. Другой подводный камень предполагает, что одна 32-битная запись MMIO является атомной по отношению к устройству; на большой шине запись может быть разделена на более мелкие транзакции.
Симбиотические отношения в архитектуре системы
Взаимодействие между регистрами ЦП и I/O на основе карты памяти является механической симпатией, которая управляет всеми вычислениями. Регистры обеспечивают скорость волдыря и немедленную манипуляцию данными, требуемую процессором, в то время как MMIO обеспечивает стандартизированный, гибкий интерфейс, необходимый для взаимодействия с физическим миром периферийных устройств. Независимо от того, пишет ли разработчик простой в UART, инициализации сложного конвейера GPU или развертывания виртуализированного сервера, они полагаются на это фундаментальное аппаратное соотношение. Освоение нюансов доступа к регистру, барьеров памяти и семантики MMIO - это то, что отделяет опытных системных программистов от экспертов, что позволяет создавать стабильное, высокопроизводительное низкоуровневое программное обеспечение.