Проектирование блока управления
Содержимое файла ROM памяти приведено ниже
v3.0 hex words addressed 0000: 00000093 0010a0a3 00100093 0010a223 00100133 0020a423 002081b3 0030a623 0008: 00310233 0040a823 004182b3 0050aa23 00520333 0060ac23 006283b3 0070ae23 0010: 00730433 0280a023 008384b3 0290a223 00000513 00000073 00000000 00000000
На рисунке 10.28 приведен итог вычисления числа Фибоначчи. Он должен соответствовать результату вычисления в линейном алгоритме.
Для завершения проектирования процессора на языке Verilog HDL необходимо вернуться в среду в ранее созданный проект RISC_V. Реализуем блок переходов BranchUnit. Как видно из рисунка 10.23 в его основе лежат компараторы, выполняющие сравнение двух 32-х битных кодов. В зависимости от кода на шине funct3 коммутирующие результирующий сигнал сравнения на выход. На языке Verilog HDL данная схема может быть реализована так как показано в листинге 10.4 (файл BranchUnit.v)
module BranchUnit( input [31:0] srcA, srcB, //входы данных с регистров input [2:0] funct3, // вход funct3 output reg branchTaken //выход сигнализирующий о наличии перехода ); always @(*) begin case (funct3) 3'b000: branchTaken = (srcA == srcB); // BEQ 3'b001: branchTaken = (srcA != srcB); // BNE 3'b100: branchTaken = ($signed(srcA) < $signed(srcB)); // BLT 3'b101: branchTaken = ($signed(srcA) >= $signed(srcB)); // BGE 3'b110: branchTaken = (srcA < srcB); // BLTU 3'b111: branchTaken = (srcA >= srcB); // BGEU default: branchTaken = 1'b0; // по умолчанию выход 0 endcase end endmoduleЛистинг 10.4.
На рисунке 10.29 приведен вид RTL созданного модуля. Развернув его (рисунок 10.30) , можно убедится в эквивалентности схемы, приведенной на рисунке 10.23.
Реализуем модуля дешифратора операций АЛУ. Для этого создадим файл ALUDecoder.v. В листинге 10.5 приведен код данного модуля. Анализируя поля funct7, funct3 инструкции RISC-V, а также поле aluOp формируемое блоком ControlUnit данный дешифратор осуществляет формирование кода микрооперации в АЛУ.
module ALUDecoder( input [6:0] funct7, input [2:0] funct3, input [1:0] aluOp,//код операции output reg [3:0] aluControl ); always @(*) begin case (aluOp) 2'b00: aluControl = 4'b0000; // операции загрузка/сохранение (ADD) 2'b01: aluControl = 4'b0001; // операция ветвления (SUB) 2'b10: begin // R-type инструкции case ({funct7, funct3}) 10'b0000000000: aluControl = 4'b0000; // ADD 10'b0100000000: aluControl = 4'b0001; // SUB 10'b0000000111: aluControl = 4'b0010; // AND 10'b0000000110: aluControl = 4'b0011; // OR 10'b0000000100: aluControl = 4'b0100; // XOR 10'b0000000001: aluControl = 4'b0101; // SLL 10'b0000000101: aluControl = 4'b0110; // SRL 10'b0100000101: aluControl = 4'b0111; // SRA 10'b0000000010: aluControl = 4'b1000; // SLT 10'b0000000011: aluControl = 4'b1001; // SLTU default: aluControl = 4'b0000; endcase end default: aluControl = 4'b0000; endcase end endmoduleЛистинг 10.5.
На рисунке 10.31 приведен вид RTL созданного модуля.
Следующим этапом выполним построение "сплиттера" кода инструкции - дешифратора инструкции (схемотехническая реализация разборки кода инструкции рассмотрена нами в начале данного раздела). Создадим в проекте RISC_V новый файл с именем InstructionDecoder.v. Внесем в него код из листинга 10.6.
module InstructionDecoder( input [31:0] instr, output [6:0] opcode, output [4:0] rd, output [2:0] funct3, output [4:0] rs1, rs2, output [6:0] funct7, output [31:0] imm ); assign opcode = instr[6:0];// выделяем 0 по 6 бит под opcode assign rd = instr[11:7]; //с 7 по 11 бит адрес регистра назначения assign funct3 = instr[14:12]; //с 12 по 14 бит поле funct3 assign rs1 = instr[19:15];//с 15 по 19 бит адрес регистра источника 1 assign rs2 = instr[24:20];//с 20 по 24 бит адрес регистра источника 2 assign funct7 = instr[31:25]; assign imm = { {21{instr[31]}}, instr[30:20] }; //формируемое поле //непосредственных значений Immediate (I-Type) endmoduleЛистинг 10.6.
На рисунке 10.32 приведен вид RTL созданного модуля.
Если раскрыть модуль InstructionDecoder, то можно наблюдать разветвление эквивалентное разветвителю, приведенному на рисунке 10.12 (рисунок 10.33).