Микропроцессоры и микроконтроллеры

Основные типы больших интегральных схем для микропроцессорных комплектов. Типовая архитектура суперскалярного микропроцессора. Мультискалярная модель выполнения программы. Доступ к памяти. Организация интерфейса МП устройств с внешними устройствами.

Рубрика Программирование, компьютеры и кибернетика
Вид курс лекций
Язык русский
Дата добавления 01.07.2016
Размер файла 8,8 M

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

Для изложения одного из алгоритмов поддержки когерентности кэшей, известного как DASH, зададим некоторые начальные условия и введем определения.

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

С каждой строкой в резидентном для нее модуле свяжем три ее возможных глобальных состояния:

1) "некэшированная", если копия строки не находится в кэше какого-либо другого модуля, кроме, возможно, резидентного для этой строки;

2) "удаленно-разделенная", если копии строки размещены в кэшах других модулей;

3) "удаленно-измененная", если строка изменена операцией записи в каком-либо модуле.

Кроме этого, каждая строка кэша может находиться в одном из трех локальных состояний:

1) "невозможная к использованию";

2) "разделяемая", если есть неизмененная копия, которая, возможно, размещается также в других кэшах;

3) "измененная", если копия изменена операцией записи,

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

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

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

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

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

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

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

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

Кэш-память с обратной записью создает меньшую нагрузку на шину процессора и обеспечивает большую производительность, однако контроллер для write-back кэша значительно сложнее.

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

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

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

Расслоение памяти. Другим структурным способом уменьшения времени доступа к памяти служит расслоение памяти. В предположении, что выборка из памяти выполняется по последовательным адресам возможно использование k блоков памяти с размещением в блоке i, где i=0,…, k-1, слов с адресами n=i+krrp mod k, где р=0,1,..., М. В этом случае возможно k параллельных обращений в память по адресам, принадлежащим различным блокам. Поэтому выборка команд программы за исключением команд, выбираемых как результат ветвления, может быть ускорена применением расслоения памяти. Аналогично может быть ускорена обработка массивов данных, последовательные элементы которых помещаются в разные блоки памяти.

Многоуровневая иерархия и расслоение памяти могут использоваться совместно.

CISC- и RISC- процессоры.

Аппаратные возможности процессоров определяются уровнем развития микроэлектронной технологии, которая ограничивает число логических элементов, размещаемых на кристалле. Фактически его размеры и число размещаемых на нем транзисторов почти целиком определяют тот набор аппаратных устройств, которые включаются в создаваемый процессор. Разработчики стремятся ввести в его состав как можно большее число различных арифметических и логических устройств, сопроцессоров и т.д., поскольку очевидно, что аппаратная реализация какой-либо функции обеспечивает более быстрое ее выполнение, чем программная. Да и длина кода для реализуемых аппаратным путем функций существенно меньше, поэтому в 80-х годах процессоры "росли вширь", оснащались новыми узлами, совершенствуя уже имеющиеся. Кроме того, разработчикам процессоров приходилось постоянно подстраиваться под стремление программистов иметь возможно более мощную и гибкую систему команд. Такие системы команд с разнообразными способами адресации и наборами условных переходов и вызовов подпрограмм, строковыми операциями и префиксациями обеспечивали создание коротких программ, гарантировали меньшее число ошибок в них. И разработчики не могли не считаться с этим.

! Под архитектурой в данном случае следует понимать только систему команд, играющую роль интерфейса между аппаратным и программным обеспечением.

Первые ростки противоречий, приведших к тому, что пути занимающихся разработкой и производством мп компаний разошлись в разные стороны, обнаружились давно. За первые 10 лет существования мп сформировалась архитектура сisc (complex instruction set computing -- вычисления со сложным набором инструкций). В построенных по её принципам процессорах более 2/3 кристалла занимают логика, реализующая выборку и декодирование сложных инструкций, прямое взаимодействие не только с регистровой памятью, но и с внешней, а также изощрённые схемы адресации. При этом блоки, непосредственно занятые вычислениями занимают лишь 1/3 площади кристалла. На протяжении нескольких десятилетий системы команд эволюционировали, находясь в сложной зависимости от стоимости аппаратных ресурсов, и прежде всего самого дорогого из них - оперативной памяти. Инженерами руководило стремление сократить размер программ, для этого они старались вложить как можно больше функциональности в одну команду. К началу 80-х число таких команд достигло почти 300 сотен, и почти каждая из них имела до 6 типов адресации. К тому же команды не имели фиксированной длины.

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

Осознав указанные факты, многие исследователи уже в 80-е годы начали поиски альтернативных путей повышения производительности процессоров. Было ясно, что нужно попытаться уменьшить «накладные расходы», отъедающие до двух третей полезной площади кристалла, снизив число логических элементов в них до минимума. Для этого было необходимо перенести "центр тяжести" на программные средства, оставив минимально возможное число команд, имеющих простую и регулярную структуру. Другими словами, начала формироваться стратегия "длинная программа -- короткие команды" в противовес господствующей «короткая программа -- длинные команды». Оставалось только найти разумный компромисс между ресурсами, отводимыми основным вычислительным узлам процессора и устройствам управления.

И вскоре этот компромисс был найден. Вначале джон кук из ibm research labs на своем экспериментальном компьютере "model 801" показал, что при использовании в программе лишь простейших команд формата «регистр--регистр» (операнды извлекаются исключительно из регистров и после выполнения операции помещаются только в регистры) скорость выполнения большинства вычислительных задач возрастает в два-три раза. Затем учеными стенфордского университета было показано, что исключение из набора команд сложных операций уменьшает число элементов процессора на порядок за счет упрощения схем управления ресурсами процессора (при этом производительность системы остается практически неизменной).

Опыт многочисленных исследователей статистических свойств программ выявил, что 80 % процессорного времени обычно тратится на выполнение всего 20 % от общего числа инструкций обычных cisc-процессоров. Причем все эти 20 % приходятся на команды типа "регистр--регистр".

И наконец, в работах д. Паттерсона и к. Секуина были сформулированы четыре основных принципа, которые должны быть положены в основу процессоров группы risc (reduced instruction set computing - вычисления с сокращенным набором команд):

А) любая операция, к какому бы типу она ни принадлежала, должна выполняться за один такт;

Б) операции обработки данных реализуются только в формате “регистр--регистр”. Обмен между памятью и регистрами (т. Е. Модификация памяти и чтение из нее необходимых данных) осуществляется только лишь с помощью специальных команд чтения/записи;

В) система команд должна содержать минимальное число наиболее часто используемых простейших команд одинаковой длины:

Г) состав системы команд должен быть оптимизирован с учетом требований компиляторов языков высокого уровня.

Кстати, над идеями, вошедшими в концепцию risc, трудились не только на западе. Ещё в 70-х годах, когда американские учёные только приблизились к формулировке того, что стало называться risc, в советском союзе уже были построены большие эвм, в которых были реализованы решения, позволившие использовать risc-архитектуру в области высокопроизводительных вычислений. Например, суперскалярная архитектура была реализована у нас 1978 году, в то время как американцы подошли к ней лишь в начале 90-х годов. А ведь именно суперскалярность называют сегодня 5-ым критерием принадлежности процессора к risc-архитектуре.

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

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

Следующая особенность risc-процессоров - высокая степень дробления конвейера. В рассмотренном выше примере он состоит из пяти ступеней: извлечения кода операции, декодирования, подготовки операндов, исполнения, сохранения результата. Реально risc-процессоры характеризуются семидесяти ступенчатыми конвейерами. С увеличением числа ступеней действия, выполняемые на каждой из них, все более и более упрощаются. Последнее означает, что уменьшается число необходимых для этого логических элементов и появляется возможность повысить тактовую частоту процессора.

Третья особенность, вытекающая из предыдущей, - развитые средства прогнозирования ветвлений и переходов. В программах для семейства х86 команда перехода встречается в среднем через каждые шесть, в программах для risc-процессоров, команды которых проще, - через каждые 10... 12 команд. Встретив команду условного перехода, процессор должен сделать предположение о том, выполнится условие или нет, и в зависимости от этого начать предвыборку команд либо с адреса предполагаемого перехода, либо с адреса, следующего за текущим. Если переход предсказан неправильно, процессору необходимо удалить со всех ступеней конвейера команды, относящиеся к неверно сделанному предположению, и перезагрузить конвейер. Это особенно сказывается на работе суперскалярных процессоров - на разных ступенях трех-четырех конвейеров может находиться довольно много команд. Их удаление с последующей перезагрузкой приводит к тому, что в течение нескольких тактов конвейер не покидает ни одна команда. Процессор, в котором это случается часто, теряет 20... 30 % своей производительности. Поэтому risc-процессоры характеризуются весьма эффективными механизмами предсказания ветвлений. Кроме того, они содержат устройства, позволяющие выбрать те из команд в предсказанном переходе, которые можно выполнить прежде, чем станет ясно, правильно ли был предсказан переход.

Еще одна особенность risc-процессоров - использование большого числа регистров. Как правило, в risc-процессорах их не менее 32. Подобная свобода (семейство х86 имеет всего восемь регистров общего назначения) позволяет снизить число обращений к относительно медленной оперативной памяти в полтора-два раза (в сравнении с cisc-процессорами), что опять-таки положительно сказывается на росте реальной производительности системы. Добавим к этому, что все risc-процессоры содержат системы управления кэш-памятью второго уровня, позволяющие работать с ней на максимальной скорости (в risc-изделиях взаимодействие с кэш-памятью обычно происходит на частоте, более низкой, чем частота самого процессора).

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

Развитие микропроцессоров происходит при постоянном стремлении сохранения преемственности программного обеспечения (по) и повышения производительности за счет совершенствования архитектуры и увеличения тактовой частоты. Сохранение преемственности по и повышение производительности, вообще говоря, противоречат друг другу. Процессоры с системой команд х86, относящиеся к классу cisc-процессоров, имеют более низкие тактовые частоты по сравнению с микропроцессорами ведущих компаний-изготовителей risc-процессоров. Существуют приложения, на которых производительность х86 микропроцессоров значительно ниже. Чем у risc-процессоров, реализованных на той же элементной базе. Однако возможность использования совместимого по для различных поколений х86 процессоров, выпущенных в течение последнего десятилетия, обеспечивает им устойчивое доминирующее положение на рынке.

В настоящее время на основе разработок компаний nexgen и amd, подхваченных компанией intel, предпринята попытка решить проблему повышения производительности в рамках архитектуры х86. Эти компании, сохраняя преемственность по системе команд с cisc-микропроцессорами семейства х86, создают новые устройства с использованием элементов risc-архитектуры, использующие концепцию разделенной (decoupled) архитектуры и risc ядра. В микропроцессор встраивается аппаратный транслятор, превращающий команды х86, в команды risc-процессора. При этом одна команда х86 может порождать до четырех команд risc-процессора. Исполнение команд происходит как в развитом суперскалярном процессоре.

Лекция 5

Методы ускорения переключения контекста процессора

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

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

Уменьшение количества сохраняемых регистров ведет к снижению производительности и, вообще говоря, находится в противоречии со стремлением увеличения производительности за счет использования быстрой регистровой памяти и параллельного функционирования устройств процессора, каждое из которых содержит собственные регистры. Однако, этот прием применяется в ряде архитектур, например в транспьютерах компании INMOS и Java-процессорах. Это архитектуры, основанные на операциях со стеком. К числу сохраняемых регистров относятся указатели на текущую позицию стека, на используемую область памяти и т.д. Число таких указателей ограничено, например, в транспьютере их 8, что позволило переключать контекст за 2 микросекунды при тактовой частоте 10 МГц.

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

Например, в архитектуре SPARC микропроцессоров компании SUN используется 200 регистров, образующих 8 групп (окон) по 32 регистра с общими для двух соседних окон восемью регистрами. Перекрытие регистровых окон выполнено так, что регистры с номерами 24-31 предыдущего окна служат одновременно регистрами с номерами 0-7 последующего окна. Это, по мнению разработчиков, увеличивает эффективность передачи параметров подпрограммам.

Использование регистров обработки быстрых прерываний. Ряд функций, связанных с обработкой прерываний, выполняются специализированными программами, которые могут использовать ограниченное число регистров. Поэтому в ряде процессоров вводятся специальные регистры, используемые при обработке так называемых быстрых прерываний. Это, например, прерывание по приему очередного символа сообщения, переносящее символ в память. Действия по началу приема, требующие запуска механизма распределения памяти, и завершению приема всего сообщения требуют, как правило, обычного прерывания, сохраняющего все регистры процессора. Но так как чаще происходит прием очередного символа, то быстрые прерывания дают существенное увеличение производительности.

Стандартизация архитектур микропроцессоров

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

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

Создание сложных новых систем требует, помимо всего прочего, наличия двух обязательных этапов: адекватного описания системы и исчерпывающего тестирования на соответствие этому описанию. Тестирование должно быть доказательным. Не прибегая к примерам из области создания больших прикладных систем, укажем на широко известные ошибки в микропроцессорах известных компаний и на наличие не декларированных возможностей микропроцессоров и операционных систем.

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

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

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

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

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

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

Известны по крайней мере две попытки реализации этого подхода, рассмотренные ниже.

Архитектурно независимая спецификация программ. В настоящее время в рамках международной организации ISO/IEC в комитете по микропроцессорным системам ведется подготовка проекта стандарта ANDF на архитектурно независимый формат спецификации программ (Architecture Neutral Distribution Format). По мнению разработчика компании Х/Ореn Company Ltd., этот формат спецификаций позволит решить проблему переносимости программ. Компиляция исходного кода предполагается двухэтапной. На первом этапе исходный код транслируется в обобщенные декларации интерфейсов прикладных программ (API) в совокупности с обобщенными описаниями типов данных. Фактически полученная оттранслированная программа представляет собой выражение абстрактной алгебры, определенной Architecture Neutral Distribution Format. В результате текст программы может быть подвергнут формальной проверке и преобразованию. На втором этапе генерируется программа для конкретной архитектуры.

Java-технология, предложенная компанией SUN. В основе данной технологии лежит понятие виртуальной Java-машины, спецификации которой включают следующие типы данных:

* byte - байт;

* short - двухбайтовое целое;

* integer - четырехбайтовое целое;

* long - восьмибайтовое вещественное;

* float - четырехбайтовое вещественное;

* double - восьмибайтовое вещественное;

* char - двухбайтовый символ;

* object - четырехбайтовая ссылка на объект;

* returnAddress - четырехбайтовый адрес возврата. В виртуальном Java-процессоре предусмотрены следующие регистры:

* PC - счетчик команд;

* Vars - регистр для доступа к локальным переменным;

* Optop - указатель на стек операндов;

* Frame - указатель на окружение времени выполнения. Большинство команд Java-процессора имеют длину один байт, что согласуется со стековой архитектурой процессора, использующей небольшое число регистров и указателей на данные,

Использование байт-кода в процессоре Java позволяет уменьшить длину программ. Средняя длина команды составляет 1,8 байта, В последнее время ко всем ранее существовавшим доводам в пользу стандартизации архитектур добавилась практическая потребность работы в сетях типа Internet, что выдвигает требование короткого программного кода. Открытые системы, создаваемые в Internet, позволят накапливать программные продукты и конструировать системы из уже существующих.

Заметим, что архитектура виртуальной Java-машины достаточно похожа на архитектуру транспьютеров компании IMMOS, Отличие фактически состоит в добавлении элементов объектно-ориентированной технологии. Одним из препятствий на пути развития Java-технологии является низкая производительность исполнения Java-кода. Однако есть все предпосылки для преодоления этого препятствия. Например, современные процессоры с архитектурой компании Intel x86 содержат специальный блок, транслирующий сложные команды в совокупность простых команд RISC-процессора. Далее RISC-процессор исполняет эти команды, используя все преимущества RISC-подхода для достижения высокой производительности. Вполне мыслимое дело разработать подобный транслятор для Java-кода, когда байт-код будет транслироваться в команды реального процессора.

Другим возможным подходом к повышению производительности служит примененный в транспьютере Т-9000. В нем предпринята попытка при сохранении байтовой системы команд транспьютеров семейств Т-2хх, Т-4хх, Т-8хх повысить скорость исполнения за счет одновременной обработки большого числа команд при исполнении их на параллельно функционирующих обрабатывающих устройствах.

Лекция 6

ILP процессоры

Архитектура ILP- процессоров

Более высокая производительность достигается как за счёт совершенствования полупроводниковой технологии, так и за счёт увеличения плотности микросхем. Дальнейшего увеличения скорости выполнения программ можно добиться в первую очередь благодаря реализации определённого вида параллелизма. Параллелизм на уровне команд (instruction-level parallelism, ILP) стал возможен благодаря созданию процессоров и методик компиляции, которые ускоряют работу за счёт параллельного выполнения отдельных RISC-операций. Системы на базе ILP используют программы, написанные на традиционных языках высокого уровня для последовательных процессоров, а обнаружение «скрытого параллелизма» автоматически выполняется благодаря применению соответствующей компиляторной технологии и аппаратного обеспечения.

Тот факт, что эти методики не требуют от прикладных программистов дополнительных усилий, имеет крайне важное значение. Это решение резко отличается от традиционного микропроцессорного параллелизма (многопроцессорный и мультискалярный тип параллельной обработки), который предполагает, что программисты должны переписывать свои приложения. Поэтому сейчас, параллельная обработка на уровне команд, является единственным надёжным подходом, позволяющим добиться увеличения производительности без фундаментальной переработки приложений. Эти два типа параллельной обработки не исключают друг друга; самые эффективные многопроцессорные или мультискалярные системы, вероятнее всего, будут создаваться на базе процессоров ILP.

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

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

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

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

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

Суперскалярные процессоры

Суперскалярные процессоры - это реализация ILP-процессора для последовательных архитектур - архитектур, программа для которых не должна передавать и, фактически, не может передавать точную информацию о параллелизме. Поскольку программа не содержит точной информации о наличии ILP, то, задача обнаружения параллелизма должна решаться аппаратурой, которая, следовательно, должна создавать план действий для обнаружения «скрытого параллелизма». Код для суперскалярных процессоров содержит последовательность команд, которая порождает корректный результат, если выполняется в установленном порядке. Код указывает последовательный алгоритм и, за исключением того, что он использует конкретный набор команд, не представляет себе точную природу аппаратного обеспечения, на котором он будет работать или точный временной порядок, в котором будут выполняться команды. Такой подход увеличивает сложность аппаратного обеспечения, в то же время суперскалярный процессор создаёт план выполнения, используя преимущества тех факторов, которые могут быть определены только во время выполнения.

Допустимые границы преобразования динамической структуры программы задают существующие на множестве инструкций отношения: зависимость по управлению и зависимость по данным. При описании архитектур суперскалярных процессоров часто используется модель окна исполнения. При исполнении программы микропроцессор как бы продвигает по статической структуре программы окно исполнения. Команды в окне могут исполняться параллельно, если между ними нет зависимости.

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

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

Все виды зависимостей по данным могут быть классифицированы по типу ассоциаций: RAR - "чтение после чтения", WAR - "запись после чтения" и WAW - "запись после записи", RAW - "чтение после записи". Пример различных зависимостей команд по данным показан на рис.11. Некоторые из зависимостей по данным могут быть устранены. RAR, по сути дела, соответствует отсутствию зависимостей, поскольку в данном случае порядок выполнения команд не имеет значения. Действительной зависимостью является только "чтение после записи" (RAW), так как необходимо прочитать предварительно записанные новые данные, а не старые.

Лишние зависимости по данным появляются в результате "записи после чтения" (WAR) и "записи после записи" (WAW).

После удаления лишних зависимостей по управлению и данным команды могут исполняться параллельно. Формирование расписания параллельного выполнения команд возлагается на аппаратные средства микропроцессора. Это расписание учитывает существующие зависимости между командами и имеющиеся функциональные модули процессора.

Рис.11. Зависимости команд по данным

Типовая архитектура суперскалярного микропроцессора представлена на рис.12.

Рис.12. Архитектура суперскалярного микропроцессора

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

Рассмотрим основные приемы повышения быстродействия в суперскалярных микропроцессорах.

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

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

Исполнение условных ветвлений состоит из следующих этапов:

* распознание команды условного ветвления;

* проверка выполнения условия перехода;

* вычисление адреса перехода;

* передача управления, в случае перехода.

На каждом этапе используются специальные приемы повышения производительности:

1. Для быстрого декодирования используются либо дополнительные биты в поле команды, либо преддекодирование команд при выборе из кэш-памяти команд.

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

Другие предсказатели используют динамически формируемую информацию в процессе исполнения программы. Обычно это информация, касающаяся истории выполнения данного ветвления, сохраняемая в таблице ветвлений или в таблице предсказаний ветвлений. Таблица предсказания ветвлений организуется по ассоциативному принципу, подобно кэш-памяти, ее элементы доступны по адресу команды, ветвление которой предсказывается. В некоторых реализациях элемент таблицы предсказания ветвления является счетчиком, значение которого увеличивается при правильном предсказании и уменьшается при неправильном. При этом значение счетчика определяет преобладающее направление ветвлений.

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

3. Для определения адреса ветвления обычно требуется выполнить целочисленное сложение, прибавляющее к текущему значению счетчика команд смещение, заданное в поле команды ветвления. И хотя это не требует дополнительных циклов для обращения к регистрам, ускорение вычисления адреса может быть достигнуто благодаря использованию буфера, содержащего ранее использованные адреса переходов.

Если требуется осуществить смену значения счетчика команд, то необходим, по крайней мере, один такт для распознания команды ветвления, модификации счетчика команд и выборки команды по заданному значению счетчика команд. Эти задержки вызывают пустые такты в конвейерах процессора. Более сложные решения используют буферы, содержащие наборы команд для двух возможных результатов ветвлений.

Возможно также использование "отложенных переходов", когда одна или несколько команд после команды ветвления выполняются безусловно.

Декодирование команд, переименование ресурсов и диспетчеризация. На этой фазе определяются существенные зависимости (RAW) по данным между командами и преодолеваются несущественные (WAW, WAR), производится распределение команд по буферам команд функциональных устройств.

При декодировании команды создается одна или несколько упорядоченных троек, каждая из которых включает: 1) исполняемую операцию, 2) указатели на операнды, 3) указатель на место помещения результата.

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

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

В первом, физический файл регистров больше логического. При необходимости переименования из списка свободных физических регистров берется один и ему сопоставляется соответствующее логическое имя. Если список свободных регистров пуст, диспетчеризация команд приостанавливается до момента появления свободных физических регистров.

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

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

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

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

В идеале команда готова к исполнению как только готовы ее входные операнды. Однако есть ряд ограничений, связанных с доступностью физических ресурсов, таких как исполнительные устройства, коммутаторы и порты регистровых файлов (или переупорядочивающего буфера). Для организации окна исполнения используются различные методы: одной очереди, многих очередей или метод резервирующей станции.

Если имеется одна очередь, то переименование регистров не требуется, так как доступность значений операндов может отмечаться битом резервирования, сопоставленным каждому регистру. Регистр резервируется, когда модифицирующая его команда назначается на исполнение. И регистр освобождается, когда заканчивается исполнение команды. Если для команды ресурсы не были зарезервированы, то она приостанавливает свое исполнение.

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

Третий метод предполагает использование резервирующей станции, состоящей из совокупности элементов, каждый из которых содержит позиции для размещения кода операции, наименования первого операнда, самого первого операнда, признака доступности первого операнда, наименования второго операнда, самого второго операнда, признака доступности второго операнда и наименования регистра результата. Когда команда завершает исполнение и вырабатывает результат, то наименование результата сравнивается с наименованиями операндов в резервирующей станции.

Если в резервирующей станции обнаруживается команда, ждущая этого результата, то данные записываются в соответствующую позицию и устанавливается признак их доступности. Когда у команды доступны все операнды, инициируется ее исполнение. Резервирующая станция следит за доступностью операндов. Когда команда при диспетчеризации попадает в резервирующую станцию, все готовые операнды из регистрового файла переписываются в поля этой команды. Когда все операнды готовы, команда исполняется. Иногда резервирующая станция содержит не сами операнды, а указатели на них в регистровом файле или переупорядочивающем буфере.

Работа с памятью. Для вычисления адреса памяти, как правило, требуется, по крайней мере, одно сложение. После вычисления адреса может понадобиться его преобразование в физический адрес, осуществляемое буфером трансляции адресов (TLB).

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

Завершение выполнения команды. Завершающей фазой исполнения команды является фаза изменения состояния процессора в соответствии с выполненной командой. Назначение этой фазы - сохранение последовательной модели исполнения программы, при реальном параллельном выполнении отдельных команд и условном выполнении команд ветвления. Для изменения состояния процессора применяются два основных способа, причем оба основаны на использовании двух состояний: состояния, измененного в результате операции, и состояния, требуемого для восстановления.

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

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

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

На рис.13. показаны основные компоненты суперскалярного микропроцессора: функциональные модули - выполнения операций с плавающей (FPU) и фиксированной (ALU) точкой, устройство загрузки/сохранения, файлы регистров, раздельная кэш-память команд и данных, а также вспомогательные модули, обеспечивающие динамическое планирование вычислительного процесса " устройство связи с кэш-памятью 2-го уровня, блок переупорядочивания команд и блок предварительной дешифрации.

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

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

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

Рис. 13. Структура суперскалярного микропроцессора

VLIW-процессоры.

Альтернатива суперскалярной обработке - процессоры с длинным командным словом (VLIW). Использование этого метода предполагает задание в командном слове совокупности параллельно выполняемых команд. Подготовкой таких программ занимается компилятор.

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

Процессоры VLIW представляют собой пример архитектуры, для которой программа представляет точную информацию о параллелизме. Компилятор выявляет параллелизм в программе и сообщает программному обеспечению, какие операции не зависят друг от друга. Эта информация имеет важное значение для аппаратного обеспечения, поскольку в этом случае оно «знает» без дальнейших проверок, какие операции можно начинать выполнять в одном и том же такте.

...

Подобные документы

  • Функционально законченное программное управляемое устройство обработки информации, в виде одной или нескольких больших или сверхбольших интегральных схем. Функции микропроцессора Pentium, основные параметры. Технология гиперконвейерной обработки.

    учебное пособие [1,1 M], добавлен 09.02.2009

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

    контрольная работа [113,5 K], добавлен 05.09.2010

  • Подключение периферийных устройств к ЭВМ. Синхронизация выполнения программы с внешними процессами. Прерывания. Реализация механизма прерывания в х86. Прямой доступ к памяти. Шины, магистраль PCI. Процесс загрузки компьютера. Клавиатура, системный таймер.

    презентация [7,1 M], добавлен 14.12.2013

  • Общее устройство микропроцессора. Структура 64-битной подсистемы памяти. Селекция портов ввода/вывода. Особенности интерфейса микропроцессорных систем. Проектирование подсистемы памяти на базе Itanium 2. Расчёт информативности и необходимых объёмов.

    курсовая работа [3,7 M], добавлен 05.12.2012

  • Ассемблер как символический аналог машинного языка. Архитектура микропроцессора: организация памяти, способы адресации операндов, правила использования регистров. Текст программы. Этапы программирования на ассемблере, алгоритмы выполнения задач.

    контрольная работа [515,1 K], добавлен 20.01.2016

  • Архитектура микроконтроллеров семейства Mega. Организация памяти. Способы адресации памяти данных. Энергонезависимая память данных. Таблица векторов прерываний. Счетчик команд и выполнение программы. Абсолютный вызов подпрограммы. Сторожевой таймер.

    дипломная работа [213,9 K], добавлен 02.04.2009

  • Архитектура ЭВМ и ее основные свойства. Классификационные признаки ЭВМ. Принципы цифрового представления информации, адресности, программного управления. Структура ЭВМ по Джону фон Нейману. Программная модель микропроцессора, классификация процессоров.

    презентация [1,0 M], добавлен 09.11.2013

  • Классификация микропроцессоров по числу больших интегральных схем, по назначению и виду обрабатываемых входных сигналов. Устройства, входящие в состав микропроцессора. Составление электронной таблицы "Ведомость расчета амортизационных отчислений".

    курсовая работа [27,8 K], добавлен 04.03.2011

  • Основа современной компьютерной техники - микропроцессоры. Увеличение их быстродействия позволяет ставить перед техникой такие задачи, как моделирование сложных процессов, обработка больших объемов информации, обеспечение автономной работы устройств.

    курсовая работа [2,6 M], добавлен 08.11.2010

  • Методы расчета, схемотехнического проектирования и конструирования элементов и блоков ЦВМ. Разработка регистра, схемы записи и считывания из оперативной памяти. Применение макроопределений при моделировании устройств и построении принципиальных схем.

    курсовая работа [1,1 M], добавлен 12.02.2013

  • Понятия и принцип работы процессора. Устройство центрального процессора. Типы архитектур микропроцессоров. Однокристальные микроконтроллеры. Секционные микропроцессоры. Процессоры цифровой обработки сигналов. Эволюция развития микропроцессоров Intel.

    реферат [158,8 K], добавлен 25.06.2015

  • Объем двухпортовой памяти, расположенной на кристалле, для хранения программ и данных в процессорах ADSP-2106x. Метод двойного доступа к памяти. Кэш-команды и конфликты при обращении к данным по шине памяти. Пространство памяти многопроцессорной системы.

    реферат [28,1 K], добавлен 13.11.2009

  • Внутренняя архитектура микропроцессора Intel 486. Формат данных и команд. Регистры общего назначения. Программная модель устройства FPU, регистр флагов. Разработка структуры и микропрограммы микропроцессора, управляющего автомата с жесткой логикой.

    курсовая работа [1,6 M], добавлен 27.05.2013

  • Функциональная схема микропроцессора Intel 8086 (i8086). Формирование физического адреса памяти, выборка команд из памяти и запись их в очередь команд. Система команд процессора. Суть защищенного режима, переход из защищенного режима в реальный режим.

    практическая работа [93,3 K], добавлен 24.03.2013

  • Микропроцессор как универсальное устройство для выполнения программной обработки информации. Функциональные возможности и архитектурные решения. Микроконтроллеры в системах управления и обработки информации. Классификация электронно-вычислительных машин.

    курсовая работа [189,6 K], добавлен 12.10.2015

  • Стёковая организация памяти как главная идея языка Форт. Вычислительная модель, лежащая в его основе. Особенности работы со стёком памяти. Константы, переменные и работа с памятью. Организация диалога в Форте. Организация памяти и организация подпрограмм.

    курсовая работа [29,9 K], добавлен 29.01.2010

  • Назначение программы, ее пользователи, основные функции и цели, которые преследуются при разработке. Теоретические основы разработки, выбор инструментальных средств. Разработка пользовательского интерфейса. Архитектура программы, основные функции.

    курсовая работа [1,1 M], добавлен 04.04.2012

  • Назначение и разновидности постоянных запоминающих устройств (ПЗУ). Конструкция и виды полупроводниковых ПЗУ. История разработки и типы Flash-памяти, ее программирование и структурная организация. Характеристика современных стандартов карт памяти.

    презентация [933,6 K], добавлен 11.12.2013

  • Обеспечение непосредственной связи контроллера прямого доступа к памяти (ПДП) и памяти микроЭВМ. Совместное использование шин системного интерфейса процессором и контроллером. Последовательность и алгоритм программирования контроллера прямого доступа.

    реферат [122,6 K], добавлен 13.11.2009

  • Внутренний кэш. Смешанная и разделенная кэш-память. Статическая и динамическая память. TLB как разновидность кэш-памяти. Организация кэш-памяти. Отображение секторов ОП в кэш-памяти. Иерархическая модель кэш-памяти. Ассоциативность кэш-памяти.

    курсовая работа [229,1 K], добавлен 04.11.2006

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.