Вариант 7
Содержание |
Разработка и VHDL описание MIPS CPU с вычислительным конвейером и КЭШем
Введение
В этой работе реализовывается пятиступенчатый конвейер MIPS CPU с КЭШем. Разработанный процессор поддерживает 15 команд включая 7 команд R типа, 7 I типа и 1 J типа. Это полностью удовлетворяет базовым идеям простого MIPS CPU включая выполнение команд, конвейерность и поддержку КЭШа.
Архитектура MIPS
Архитектура, разработанная компанией MIPS Computer Systems (в настоящее время MIPS Technologies) в соответствии с концепцией проектирования процессоров RISC (то есть для процессоров с сокращенным набором команд). Ранние модели процессора имели 32-битную структуру, позднее появились его 64-битные версии. Существует множество модификаций процессора, включая MIPS I, MIPS II, MIPS III, MIPS IV, MIPS V, MIPS32 и MIPS64, из них действующими являются MIPS32 (для 32-битной реализации) и MIPS64 (для 64-битной реализации). MIPS32 и MIPS64 определяют как набор регистров управления, так и набор команд. Помимо этого, доступны дополненные модели, например, MIPS-3D, включающий в себя набор SIMD команд для обработки чисел с плавающей точкой, предназначенный для решения простых 3D задач, MDMX (MaDMaX) — с ещё более широкими возможностями — набором SIMD команд и использующий 64-битные регистры с плавающей точкой для работы с целыми числами, MIPS16e, который сжимает поток команд, чтобы уменьшить объём памяти, занимаемый программами, а также MIPS MT, обеспечивающий многопотоковый режим обработки.
Таблица 1.1. Управляющее слово (указываются биты)
0-4 | 5 | 6 | 7 | 8 | 9-13 | 14 | 15 | 16-17 | 18-32 |
---|---|---|---|---|---|---|---|---|---|
ALUCONTROL | ALUSRCA | WRITEMEM | MEMTOREG | WRITEREG | ResltDes | ALURESOK | MEMRESOK | ALUSRCB | |
Функциональный код ALU | Источник данных ALU данные A | Управление записью Разрешить запись в память | Должен ли регистровый файл записать данные из памяти | Должен ли результат быть записан в регистровый файл в фазе WB | Управление регистром, чтобы записать в фазе WB | Использует ли фаза EXE ALU | Нужен ли фазе MEM доступ к памяти | Источник данных ALU данные B | Не используются |
Задача контроллера – декодировать MIPS инструкции и сгенерировать сигналы управления для каждого элемента в CPU. Генерация сигналов управления является ключевой задачей при разработке CPU. Она координирует работу каждой части процессора, чтобы работать в фазе друг с другом. Решение проблем конфликтов также реализовано в контроллере. В данной курсовой работе все функции контроллера наряду с декодированием инкапсулированы в PCU. Тип команды распознается декодированием кода операции. Различные команды обладают разными кодами операций. Например, команды R типа обладают нулевым кодом операции в данной курсовой работе. Для команд R типа, тип вычислений распознается декодированием функционального кода. Как только конкретная операция команды распознана, PCU может начать генерацию управляющего слова. Управляющее слово содержит каждый фрагмент информации, необходимой, чтобы определить поведение процессора в текущем цикле и обеспечивает достаточную информацию для генерации управляющего слова в следующем цикле. На практике управляющее слово записано в каждый регистр резервного копирования для обеспечения правильно работы каждой фазы конвейера.
На иллюстрации показан простой пятиуровневый конвейер в RISC-процессорах. Здесь:
- g0(x1,x2) = 0 - константа 0
- IF (англ. Instruction Fetch) — получение инструкции,
- ID (англ. Instruction Decode) — раскодирование инструкции,
- EX (англ. Execute) — выполнение,
- MEM (англ. Memory access) — доступ к памяти,
- WB (англ. Register write back) — запись в регистр.
Вертикальная ось — последовательные независимые инструкции, горизонтальная — время. Зелёная колонка описывает состояние процессора в один момент времени, в ней самая ранняя, верхняя инструкция уже находится в состоянии записи в регистр, а самая последняя, нижняя инструкция только в процессе чтения.
Конфликты по данным являются следствием логической зависимости команд между собой и происходят, если для выполнения очередной команды требуется результат выполнения предыдущей. В большинстве случаев, инструкция поместит свой результат в регистр или память так что следующие инструкции смогли бы воспользоваться им. Однако если каждая инструкция ожидает пока будет достигнут окончательный результат предыдущей инструкции, появится ненужная задержка и конвейер остановится. Таким образом, когда зависимость возникает, необходимо получить частичный результат предыдущей инструкции для нуждающейся инструкции, как только возможно. Такой прием называется форвардингом. В MIPS, частичные результаты имеют следующие три источника:
- Фаза ID – результат этой фазы может поступать из файла регистров или слова инструкции.
- Фаза EXE – результат этой фазы может поступать из АЛУ
- Фаза MEM - результат этой фазы может поступать из памяти (КЭШа)
Так как в данном проекте отсутствуют инструкции умножения, то конфликты по данным встречаются реже. В следующем списке приведены краткие идеи о решении конфликтов конвейера в данной разработке. Когда команде In необходимо использовать результаты команды In-3, так как In-3 достигла этапа WB и записала свой результат в регистровый файл по заднему фронту clk текущего цикла, конфликтов по данным не будет. Когда команде In необходимо использовать результаты команды In-2, так как In-2 достигла этапа MEM, результат был посчитан по заднему фронту clk текущего цикла, можно использовать форвардинг. Проблема состоит в том, что вычисленный результат еще не был записан в регистровый файл, так что мы ждем данных из регистра резервной копии этапа MEM. Когда команде In необходимо использовать результаты команды In-1 , возможны два случая. Если результат In-1 из ID или EXE этапа, то результат был вычислен и возможен форвардинг. Однако если результат In-1 из MEM он еще не готов. Единственным решением является установка пузыря, чтобы задержать выборку команд на один цикл и позволить In-1 получить свой результат из памяти.
Последний тип конфликтов – конфликты по управлению, которые возникают когда необходимо ветвление. Конфликты по управлению могут вызывать даже большие потери производительности конвейера, чем конфликты по данным. Когда выполняется команда условного перехода, она может либо изменить, либо не изменить значение счетчика команд. Если команда условного перехода заменяет счетчик команд значением адреса, вычисленного в команде, то переход называется выполняемым; в противном случае, он называется невыполняемым.
Простейший метод работы с условными переходами заключается в приостановке конвейера как только обнаружена команда условного перехода до тех пор, пока она не достигнет ступени конвейера, которая вычисляет новое значение счетчика команд. Такие приостановки конвейера из-за конфликтов по управлению должны реализовываться иначе, чем приостановки из-за конфликтов по данным, поскольку выборка команды, следующей за командой условного перехода, должна быть выполнена как можно быстрее, как только мы узнаем окончательное направление команды условного перехода.
Код
LIBRARY ieee; USE ieee.std_logic_1164.all; LIBRARY work; ENTITY mips IS port ( CLK : IN STD_LOGIC; RESET : IN STD_LOGIC; DI : IN STD_LOGIC_VECTOR(31 downto 0); INSTR : IN STD_LOGIC_VECTOR(31 downto 0); MEMWE : OUT STD_LOGIC; MEMRD : OUT STD_LOGIC; DADDR : OUT STD_LOGIC_VECTOR(31 downto 0); DATAO : OUT STD_LOGIC_VECTOR(31 downto 0); PC : OUT STD_LOGIC_VECTOR(31 downto 0) ); END mips; ARCHITECTURE bdf_type OF mips IS component alu PORT(A : IN STD_LOGIC_VECTOR(31 downto 0); B : IN STD_LOGIC_VECTOR(31 downto 0); opcode : IN STD_LOGIC_VECTOR(4 downto 0); negative : OUT STD_LOGIC; overflow : OUT STD_LOGIC; zero : OUT STD_LOGIC; output : OUT STD_LOGIC_VECTOR(31 downto 0) ); end component; component registerfile PORT(wen : IN STD_LOGIC; clk : IN STD_LOGIC; nReset : IN STD_LOGIC; rsel1 : IN STD_LOGIC_VECTOR(4 downto 0); rsel2 : IN STD_LOGIC_VECTOR(4 downto 0); wdat : IN STD_LOGIC_VECTOR(31 downto 0); wsel : IN STD_LOGIC_VECTOR(4 downto 0); rdat1 : OUT STD_LOGIC_VECTOR(31 downto 0); rdat2 : OUT STD_LOGIC_VECTOR(31 downto 0) ); end component; component nextpc PORT(branch : IN STD_LOGIC; jump : IN STD_LOGIC; se : IN STD_LOGIC; INSTR : IN STD_LOGIC_VECTOR(31 downto 0); PC : IN STD_LOGIC_VECTOR(31 downto 0); NPC : OUT STD_LOGIC_VECTOR(31 downto 0) ); end component; component mux2x32 PORT(sel : IN STD_LOGIC; data0x : IN STD_LOGIC_VECTOR(31 downto 0); data1x : IN STD_LOGIC_VECTOR(31 downto 0); result : OUT STD_LOGIC_VECTOR(31 downto 0) ); end component; component mux2x5 PORT(sel : IN STD_LOGIC; data0x : IN STD_LOGIC_VECTOR(4 downto 0); data1x : IN STD_LOGIC_VECTOR(4 downto 0); result : OUT STD_LOGIC_VECTOR(4 downto 0) ); end component; component dff32e PORT(sclr : IN STD_LOGIC; clock : IN STD_LOGIC; enable : IN STD_LOGIC; data : IN STD_LOGIC_VECTOR(31 downto 0); q : OUT STD_LOGIC_VECTOR(31 downto 0) ); end component; component maincontrol PORT(z : IN STD_LOGIC; func : IN STD_LOGIC_VECTOR(5 downto 0); opcode : IN STD_LOGIC_VECTOR(5 downto 0); jump : OUT STD_LOGIC; branch : OUT STD_LOGIC; memtoreg : OUT STD_LOGIC; writemem : OUT STD_LOGIC; writereg : OUT STD_LOGIC; regdes : OUT STD_LOGIC; alusrcb : OUT STD_LOGIC; se : OUT STD_LOGIC; aluctr : OUT STD_LOGIC_VECTOR(4 downto 0) ); end component; signal aluctr : STD_LOGIC_VECTOR(4 downto 0); signal aludatab : STD_LOGIC_VECTOR(31 downto 0); signal alusrcb : STD_LOGIC; signal branch : STD_LOGIC; signal IMMSIGN : STD_LOGIC_VECTOR(31 downto 16); signal jump : STD_LOGIC; signal memtoreg : STD_LOGIC; signal NPC : STD_LOGIC_VECTOR(31 downto 0); signal PC_ALTERA_SYNTHESIZED : STD_LOGIC_VECTOR(31 downto 0); signal R : STD_LOGIC_VECTOR(31 downto 0); signal regdatab : STD_LOGIC_VECTOR(31 downto 0); signal regdes : STD_LOGIC; signal SE : STD_LOGIC; signal writemem : STD_LOGIC; signal WRITEREG : STD_LOGIC; signal zero : STD_LOGIC; signal SYNTHESIZED_WIRE_0 : STD_LOGIC_VECTOR(31 downto 0); signal SYNTHESIZED_WIRE_1 : STD_LOGIC_VECTOR(31 downto 0); signal SYNTHESIZED_WIRE_2 : STD_LOGIC_VECTOR(4 downto 0); signal SYNTHESIZED_WIRE_3 : STD_LOGIC; signal SYNTHESIZED_WIRE_4 : STD_LOGIC; signal GDFX_TEMP_SIGNAL_0 : STD_LOGIC_VECTOR(31 downto 0); BEGIN SYNTHESIZED_WIRE_3 <= '1'; GDFX_TEMP_SIGNAL_0 <= (IMMSIGN(31 downto 16) & INSTR(15 downto 0)); b2v_instalu : alu PORT MAP(A => SYNTHESIZED_WIRE_0, B => aludatab, opcode => aluctr, zero => zero, output => R); b2v_inst1 : registerfile PORT MAP(wen => WRITEREG, clk => CLK, nReset => RESET, rsel1 => INSTR(25 downto 21), rsel2 => INSTR(20 downto 16), wdat => SYNTHESIZED_WIRE_1, wsel => SYNTHESIZED_WIRE_2, rdat1 => SYNTHESIZED_WIRE_0, rdat2 => regdatab); b2v_inst12 : nextpc PORT MAP(branch => branch, jump => jump, se => SE, INSTR => INSTR, PC => PC_ALTERA_SYNTHESIZED, NPC => NPC); b2v_inst13 : mux2x32 PORT MAP(sel => memtoreg, data0x => DI, data1x => R, result => SYNTHESIZED_WIRE_1); b2v_inst14 : mux2x5 PORT MAP(sel => regdes, data0x => INSTR(15 downto 11), data1x => INSTR(20 downto 16), result => SYNTHESIZED_WIRE_2); b2v_inst2 : dff32e PORT MAP(sclr => RESET, clock => CLK, enable => SYNTHESIZED_WIRE_3, data => NPC, q => PC_ALTERA_SYNTHESIZED); b2v_inst3 : maincontrol PORT MAP(z => zero, func => INSTR(5 downto 0), opcode => INSTR(31 downto 26), jump => jump, branch => branch, memtoreg => memtoreg, writemem => writemem, writereg => WRITEREG, regdes => regdes, alusrcb => alusrcb, se => SE, aluctr => aluctr); SYNTHESIZED_WIRE_4 <= INSTR(15) AND SE; b2v_inst5 : mux2x32 PORT MAP(sel => alusrcb, data0x => regdatab, data1x => GDFX_TEMP_SIGNAL_0, result => aludatab); IMMSIGN <= (SYNTHESIZED_WIRE_4 & SYNTHESIZED_WIRE_4 & SYNTHESIZED_WIRE_4 & SYNTHESIZED_WIRE_4 & SYNTHESIZED_WIRE_4 & SYNTHESIZED_WIRE_4 & SYNTHESIZED_WIRE_4 & SYNTHESIZED_WIRE_4 & SYNTHESIZED_WIRE_4 & SYNTHESIZED_WIRE_4 & SYNTHESIZED_WIRE_4 & SYNTHESIZED_WIRE_4 & SYNTHESIZED_WIRE_4 & SYNTHESIZED_WIRE_4 & SYNTHESIZED_WIRE_4 & SYNTHESIZED_WIRE_4); MEMWE <= writemem; DADDR <= R; DATAO <= regdatab; PC <= PC_ALTERA_SYNTHESIZED; END;
Формулы
Граф
Заключение
При разработке конвейера очень важно разрешить проблему конфликтов. От методов борьбы с конфликтами зависит правильность работы конвейера. КЭШ позволяет ускорить работу процессора.