Forth http://fforum.winglion.ru/ |
|
Конвейерный многоядерный процессор. http://fforum.winglion.ru/viewtopic.php?f=56&t=2818 |
Страница 1 из 1 |
Автор: | WingLion [ Пн мар 12, 2012 19:34 ] |
Заголовок сообщения: | Конвейерный многоядерный процессор. |
При разработке процессоров на ПЛИС фирмы ALTERA возникает ряд сложностей, связанный с особенностями встроенной блоковой памяти. Память может работать на высокой частоте, но время выборки составляет несколько тактов, в результате чего при последовательной выборке команд и данных возникают лишние задержки. На форуме уже высказывалась идея, как их обойти. Для этого можно использовать многоядерный процессор, разделение времени для ядер которого происходит потактово, и тогда каждый такт память работает с одним ядром, а следующий и предыдущий с другим. Такое построение позволяет задействовать скорость памяти полностью практически без потерь на ожидание. В то же время, скорость процессора так же зависит от разложения его действий по ступеням конвейера, в связи с чем возникла идея объединения конвейерной работы процессора с конвейерной работой памяти. Запишем работу блочной памяти ПЛИС (здесь и далее речь и блочной памяти ПЛИС ф. ALTERA) в виде следующих действий: 1. фиксация адреса. 2. Выборка данных из массива и фиксация их в выходном регистре. Фактически, это те два такта, требуемые для выборки данных из блочной памяти ПЛИС. Реально, для увеличения скорости работы памяти, приходится добавлять на выход еще один регистр, и тогда добавляется этап: 3. Фиксация выходных данных в дополнительном регистре. Теперь таким же образом запишем работу процессора с конвейером. Для простоты будем считать, что этапов конвейера всего два: 1. Фиксация команды в регистре команд 2. Дешифрация и исполнение команды - модификация "всех" регистров процессора. В случае, когда и память и процессор успевает сделать все что нужно, оба этапа объединяются в один, и получается процессор с однотактовой схемой. однако, память у нас не успевает, и процессор не редко подтормаживает на сложных командах поэтому дйствия разносим, а исполнение команды расширяем еще на один такт. Объединяя обе схемы получаем следующую последовательность действий, которую должна выполнять система процессор-память: 1. фиксация адреса. 2. Выборка данных из массива и фиксация их в выходном регистре. 3. Фиксация выходных данных в дополнительном регистре. 4. Фиксация команды в регистре команд 5. Дешифрация и исполнение команды - модификация "всех" регистров процессора. 6. Дополнительный период для исполнения команды Эта последовательность в простейшем случае должна исполняться по кругу. При этом на исполнения одной команды процессора тратится шесть тактов. Можно искать возможность оптимизации, сокращения этого круга для повышения общей скорости системы, но в данном случае этого не требуется, потому что идея данного поста, как раз и заключается в том, как получить максимум скорости от такой схемы без применения особых ухищрений. Идея в следующем. Состояние процессора определяется содержимым его регистров, и может быть помещено в некий регистр конечной длины. Возьмем 6 таких регистров, поместим в них начальные данные для шести процессоров и зациклим регистры в кольцо так, что бы каждый такт содержимое регистров сдвигалось по кругу. Закрепим за каждой позицией в круге одно из действий описанных выше, и получим в результате кольцевой шестипозиционный конвейер, который при исполнении будет за шесть тактов выполнять шесть потоков команд для шести процессоров. Общая скорость исполнения для такой системы соответствует тактовой частоте. И на каждый процесс отводится ровно 1/6 общего времени без потерь на переключение процессов. Так же стоит заметить, что главная проблема конвейера - необходимость сброса при переходах в данном случае отпадает, потому что необходимость в таком сбросе просто отсутствует. Каждая команда проходит полный цикл конвейера и исполняется фактически без задержек. |
Автор: | WingLion [ Вт мар 13, 2012 18:30 ] |
Заголовок сообщения: | Re: Конвеерный многоядерный процессор. |
Сегодня высказанная выше идея прошла практическую проверку. За основу взял свой четырехбитный EQUINOX, раскидал его логику на конвейер, подключил тестовую память и прогнал маленькую тестовую программку с симуляторе, запуская одно из шести ядер. Логику раскидывал наобум, проверяя предельную частоту, на которую разводился процессор и подглядывая, какая цепь дает наибольшую задержку. В результате ALU "рассыпалось" на три стадии, которые на конвейере без проблем отрабатываются на предельной частоте. Результаты для 6Е16 получились такие: Объем шестиядерного процессора - ~3000LCELLs Частота на циклоне-3 с максимальным спидгрейдом - 250MHz. на циклоне-3 со спидгрейдом 8 частота - 200-210 MHz. на циклоне-2 со спидгрейдом 7 частота - 200MHz. Для интереса посмотрел, что получается на 3-м стратиксе... - более 500MHz... Все варианты с включенным в систему команд умножением 16x16. |
Автор: | Hishnik [ Вт мар 13, 2012 21:38 ] |
Заголовок сообщения: | Re: Конвейерный многоядерный процессор. |
Так оно в один logic level уложилось? А сколько команд в АЛУ? |
Автор: | WingLion [ Вт мар 13, 2012 22:16 ] |
Заголовок сообщения: | Re: Конвейерный многоядерный процессор. |
Хищник писал(а): Так оно в один logic level уложилось? А сколько команд в АЛУ? в один не уложилось.. в АЛУ три ступени выполнения. А операций почти два десятка. |
Автор: | Hishnik [ Вт мар 13, 2012 23:06 ] |
Заголовок сообщения: | Re: Конвейерный многоядерный процессор. |
WingLion писал(а): в один не уложилось.. в АЛУ три ступени выполнения. А операций почти два десятка. Но если такие частоты, то между триггерами явно должно быть минимум логики. Или сами операции конвейеризованы, и три отсюда ступени? |
Автор: | WingLion [ Ср мар 14, 2012 04:30 ] |
Заголовок сообщения: | Re: Конвейерный многоядерный процессор. |
Хищник писал(а): Но если такие частоты, то между триггерами явно должно быть минимум логики. Или сами операции конвейеризованы, и три отсюда ступени? конвейер в АЛУ возникает фактически из-за необходимости мультиплексирования результатов. А сами операции выполняются за один такт на отдельных схемах. фактически отдельно есть умножитель, сумматор-вычитатель, логик (and/or/xor/nand), инкрементер/декрементер/инвертор, вычислитель флага нуля и pc+1 в доп регистре вычисляется. Сейчас вспомнил, что надо еще чистый сдвигатель в процессор добавить, а то умножением его делать, как планировал, не кузяво. А дальше - мультиплексор результатов, которые надо во время выполнения команды положить в стеки/регистры. Вот этот мультиплексор и получился самый тормозной, когда схема не упирается в предел частоты встроенной памяти. |
Автор: | WingLion [ Ср мар 14, 2012 19:13 ] |
Заголовок сообщения: | Re: Конвейерный многоядерный процессор. |
Для ясности, о чем тут идет речь и чтобы собрать мысли в кучу (короче, чтобы навести тень на плетень), привожу здесь части кода процессора. Первое - это верхний файл процессора EQUINOX: Код: --- стандартные библиотеки --- LIBRARY IEEE; USE IEEE.std_logic_1164.all; USE IEEE.std_logic_unsigned.all; --- свои библиотеки --- USE WORK.Common_lib.all; -- общая для всех USE WORK.EQUINOX_lib.all; -- библиотека для ядра процессора EQUINOX -- объявляем одиночный процессор entity EQUINOX_SINGLE is Port( clk,reset,ena : in node := vcc; -- синхро и управляющие входы addr : out word; -- адрес памяти/устройств do : out word; -- выход данных, записываемых в память/устройства di : in word := zero16; -- вход данных/команд из памяти mwr,mrd : out node; -- управление работой памяти test_top,test_bot,test_pc,test_cmd : out word -- тестовые выходы ); end EQUINOX_SINGLE; architecture rtl of EQUINOX_SINGLE is signal cpu : cpu_t; -- все регистры процессора -- объявлены в одной переменной типа record, определенной в библиотеке begin -- главный процесс process (clk) begin if reset = gnd then -- по сбросу устанавливаем счетчик команд в ноль cpu1.pc <= zero16; -- и регистр команд заполняем командами NOP cpu.cmd <= nops; elsif clk'event and clk = vcc and ena = vcc then -- по каждому разрешенному такту -- выполняем один шаг работы процессора cpu <= equinox_main(cpu,di); -- зависящий от состояния -- процессора и входных данных -- функция определена в библиотеке ядра процессора end if; -- на этом vhdl-описание процессора закончено! -- осталось только подключить выходные сигналы addr <= equinox_main(cpu,di).oadr; do <= equinox_main(cpu,di).odat; mwr <= equinox_main(cpu,di).mwr; mrd <= equinox_main(cpu,di).mrd; end process; -- и подключить тестовые сигналы test_top <= cpu1.dst.top; test_bot <= cpu1.dst.bot; test_pc <= cpu1.pc; test_cmd <= cpu1.cmd; --- теперь совсем конец end; Описанный здесь однотактный процессор нормально работает, если в качестве памяти к нему подключить нечто быстрое, выдающее результат за один такт. Например, ПЗУ на логических ячейках, порты ввода/вывода, доп-регистры на ячейках ПЛИС, и получится простейший работоспособный контроллер. С рабочей частотой 100MHz и выше. Чтобы этот процессор заработал с внутренней памятью ПЛИС, его придется подтормаживать. Если на clk подать 100MHz, то на ena придется подать 33MHz со скважностью 1:3, и схема будет работать фактически на этих 33MHz... один такт работает логическое ядро процессора, два других - производится выборка из памяти. |
Автор: | WingLion [ Ср мар 14, 2012 19:28 ] |
Заголовок сообщения: | Re: Конвейерный многоядерный процессор. |
И поэтому я делаю следующий шаг. В схему добавляются еще два набора регистров процессора, и все три набора включаются в кольцо: Опускаю шапку и конец исходника, которые фактически не меняются. Измененная часть: Код: architecture rtl of EQUINOX_3E16 is -- signal cpu : cpu_t; -- три совершенно одинаковых набора регистров signal cpu1,cpu2,cpu3 : cpu_t; begin process (clk) begin if reset = gnd then -- по сбросу все счетчики команд устанавливаются на разные адреса cpu1.pc <= zero16; cpu2.pc <= zero16+256; cpu3.pc <= zero16+512; cpu1.cmd <= zero16; -- первое ядро стартует с NOP-ов cpu2.cmd <= stops; -- два других cpu3.cmd <= stops; -- остановлены elsif clk'event and clk = vcc and ena = vcc then -- кольцо из трех наборов регистров с одним логическим ядром cpu1 <= equinox_main(cpu3,di); cpu2 <= cpu1; cpu3 <= cpu2; end if; На вход ena в такой схеме уже можно смело подавать логическую единицу, и все три ядра заработают на частоте 1/3 от частоты clk... Собственно, в симуляторе это хорошо видно. Картинки не привожу, потому что кто сам с усам, тот их сам легко получит, а кому все это не интересно, тому и картинки не интересны. |
Автор: | WingLion [ Ср мар 14, 2012 19:37 ] |
Заголовок сообщения: | Re: Конвейерный многоядерный процессор. |
Следующий шаг - это шестиядерный конвейерный вариант. Тут, собственно, интересно только это: Начальная схема, где конвейера еще и нет: Код: rchitecture rtl of EQUINOX_6E16 is --attribute altera_attribute : string; -- Attribute set on architecture, not entity --attribute altera_attribute of rtl: architecture is "-name AUTO_SHIFT_REGISTER_RECOGNITION OFF"; signal cpu : cpu_t; signal cpu1,cpu2,cpu3,cpu4,cpu5,cpu6 : cpu_t; begin process (clk) begin if reset = gnd then cpu1.pc <= zero16; cpu2.pc <= zero16+256; cpu3.pc <= zero16+512; cpu4.pc <= zero16+768; cpu5.pc <= zero16+1024; cpu6.pc <= zero16+1280; cpu1.cmd <= zero16; cpu2.cmd <= stops; cpu3.cmd <= stops; cpu4.cmd <= stops; cpu5.cmd <= stops; cpu6.cmd <= stops; elsif clk'event and clk = vcc and ena = vcc then cpu1 <= equinox_main(cpu8,di); cpu2 <= cpu1; cpu3 <= cpu2; cpu4 <= cpu3; cpu5 <= cpu4; cpu6 <= cpu5; end if; cpu <= cpu4; addr <= cpu.oadr; do <= cpu.odat; mwr <= cpu.mwr; mrd <= cpu.mrd; id <= cpu.id; end process; test_top <= cpu.dst.top; test_bot <= cpu.dst.bot; test_pc <= cpu.pc; test_cmd <= cpu.cmd; Здесь важно для понимания, что выходные шины подключены не к первому ядру, как в трехядерном варианте, а к 4му, что обеспечивает появление адреса для памяти за 2 такта до момента, когда будут нужны соответствующие данные. Вот тут, как раз, и возникает возможность повышения тактовой частоты процессора, если логику раскинуть на шесть ступеней конвейера. В схеме выше, можно сказать, что пять из шести ступеней конвейера просто ничего не делают, а все делается одной ступенью. Такое построение позволяет легко проследить логику работы кольца ядер, чтобы перейти к следующему варианту схемы... |
Автор: | WingLion [ Ср мар 14, 2012 19:53 ] |
Заголовок сообщения: | Re: Конвейерный многоядерный процессор. |
A следующий вариант - это мультиядерный процессор с параметрически задаваемым количеством ядер: Код: -- шапка та же LIBRARY IEEE; USE IEEE.std_logic_1164.all; USE IEEE.std_logic_unsigned.all; USE WORK.Common_lib.all; USE WORK.EQUINOX_lib.all; entity EQUINOX_NE16 is generic( -- параметр - количество ядер N : integer := 8 -- number of CPU cores ); Port( clk,reset,ena : in node := vcc; addr : out word; do : out word; di : in word; mwr,mrd : out node; id : out byte; -- выход идентификатора ядра test_top,test_bot,test_pc,test_cmd : out word ); end EQUINOX_NE16; architecture rtl of EQUINOX_NE16 is -- создаем новый тип-массив из записей с регистрами процессорных ядер type cpus_t is array (1 to N) of cpu_t; -- объявляем сам массив signal cpus : cpus_t; -- вспомогательная переменная signal cpu : cpu_t; -- в схеме она набор отображающий -- состояние одного из ядер процессора, а не регистров -- вспомогательные сигналы для запуска signal cnt_reset : int8; -- счетчик сброса signal rst : node; -- внутренний сброс begin process (clk) begin if reset = gnd then -- по внешнему сбросу, устанавливаем внутренний -- сброс и счетчик наколичество ядер cnt_reset <= N; rst <= gnd; elsif clk'event and clk = vcc and ena = vcc then -- по тактам перебираем все ядра от N-го до первого if cnt_reset > 0 then -- пока счетчик не обнулился, cnt_reset <= cnt_reset - 1; rst <= gnd; --держим внутренний сброс else rst <= vcc; end if; if rst = gnd then -- по внутреннему сбросу инициируем все ядра if cnt_reset /= 1 then -- все не первые стоят cpus(cnt_reset).id <= zero8+cnt_reset; cpus(cnt_reset).cmd <= stops; cpus(cnt_reset).pc <= zero16 + (cnt_reset - 1)*256; else -- а первое стартует с нулевого адреса cpus(cnt_reset).id <= zero8+cnt_reset; cpus(cnt_reset).cmd <= nops; cpus(cnt_reset).pc <= zero16; end if; else -- кольцо из ядер cpus(2 to N) <= cpus(1 to N-1); -- и логическое ядро cpus(1) <= equinox_main(cpus(N),di); end if; end if; -- вспомогательный набор регистров соотвтетствует ядру, которое -- через 2 такта будет ожидать свои данные на шине di cpu <= cpus(N-2); -- для него и выводим наружу адрес, данные, -- управляющие шины и идентификатор addr <= cpu.oadr; do <= cpu.odat; mwr <= cpu.mwr; mrd <= cpu.mrd; id <= cpu.id; end process; -- тестовые шины уже можно и выкинуть test_top <= cpu.dst.top; test_bot <= cpu.dst.bot; test_pc <= cpu.pc; test_cmd <= cpu.cmd; end; Такая вот, штучка.... И замечу, что Quartus при включении некоторых опций оптимизации сам начинает раскидывать логическое ядро по конвееру, что видно по увеличению тактовой частоты, на которую разводится проект. Без оптимизации начальная частота - 89MHz, a при ее включении, частота подымается до 144MHz еще без ручного раскидывания операций по конвейеру. |
Страница 1 из 1 | Часовой пояс: UTC + 3 часа [ Летнее время ] |
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group http://www.phpbb.com/ |