Разработка простейшего декодера команд RISC-V

Рис. 4.4. Базовые форматы команд RISC-V, показывающие варианты размещения immediate с слове инструкции
Ну и самое "весёлое" - собирать immediate по слову инструкции. Вишенкой на торте великой и свободной архитектуры RV, победно шествующей по миру - является именно кодировка в командах констант и непосредственных значений. Она вызывает прям бурю эмоций и восторга своей красотой и элегантностью технических решений. Небольшая шпаргалка на эту тему есть в спецификации - рис.4.5.
![Непосредственные значения, создаваемые инструкциями RISC-V. Поля помечены битами команд, используемыми для построения их значения. Расширение знака всегда использует бит inst[31]](/EDI/23_04_25_1/1745360398-24385/tutorial/1378/objects/4/files/04-05.jpg)
Рис. 4.5. Непосредственные значения, создаваемые инструкциями RISC-V. Поля помечены битами команд, используемыми для построения их значения. Расширение знака всегда использует бит inst[31]
Расширение знака всегда использует inst[31]. Расширение знака является одной из наиболее важных операций с immediates (особенно в RV64I), и в RISC-V бит знака для всех immediate всегда хранится в бите 31 инструкции, чтобы позволить расширению знака выполняться параллельно с декодированием команды.
Один из удобных инструментариев полезных при освоении ассемблера RISC-V, а также для целей тестирования разрабатываемых узлов процессора - симулятор RV32 - RARS- http://github.com/TheThirdOne/rars/releases - чтоб спокойно генерировать бинарные опкоды команд, а не писать их вручную с риском ошибиться в паре-тройке бит.
Декомпозиция…
Попробуем разбить декодер на несколько блоков для упрощения описания каждого из них, и для возможно более простой дальнейшей модификации. По факту - надо из кода инструкции выделить сам опкод, коды доп функций команд, адреса регистров и непосредственные значения декодируем отдельно, валидные значения декодированных полей отдельными сигналами.
Таким образом, декодер составится из блоков:
- формирователь непосредственных значений;
- декодер адресов регистров-операндов и функциональных полей.
Формирователь непосредственных значений
Формирователь непосредственных значений - принимает на вход 32 бита инструкции, на выходе формирует 32-битное непосредственное значение и сигнал "валидности" значения - 1, если из инструкции декодирован immediate, и 0, если его в команде нет - например, для команд R-типа.
Код:
module rv_imm
( input [31:0] inst,
output reg [31:0] imm,
output reg imm_en
);
wire [6:0] opcode;
wire [19:0] sign;
//wire zero;
assign opcode[6:0] = inst[6:0];
assign sign = {
inst[31],inst[31],inst[31],inst[31],inst[31],//inst[31],
inst[31],inst[31],inst[31],inst[31],inst[31],//inst[31],
inst[31],inst[31],inst[31],inst[31],inst[31],//inst[31],
inst[31],inst[31],inst[31],inst[31],inst[31]//,inst[31]
};
//assign zero = 1'b0;
always @ *
begin
case (opcode)
//I-type
7'b00000_11 : begin
imm <= {sign,inst[31:20]}; $display ("LOAD");
imm_en <= 1'b1;
end
7'b00011_11 : begin
imm <= {sign,inst[31:20]}; $display ("Misc-MEM");
imm_en <= 1'b1;
end
7'b00100_11 : begin
imm <= {sign,inst[31:20]}; $display ("OP-Imm");
imm_en <= 1'b1;
end
7'b11100_11 : begin
imm <= {sign,inst[31:20]}; $display ("SYSTEM");
imm_en <= 1'b1;
end
7'b11001_11 : begin
imm <= {sign,inst[31:20]}; $display ("JALR");
imm_en <= 1'b1;
end
//J-type
7'b11011_11 : begin
imm <= {sign[19:8],inst[19:12],inst[20],inst[30:25],inst[24:21],1'b0}; //J-type
$display("JAL");
imm_en <= 1'b1;
end
//S-type
7'b01000_11 : begin
imm <= {sign,inst[31:25],inst[11:7]};
$display("Store");
imm_en <= 1'b1;
end
//U-type
7'b01101_11 : begin
imm <= {inst[31:12],12'b0};
$display("LUI");
imm_en <= 1'b1;
end
7'b00101_11 : begin
imm <= {inst[31],inst[30:20],inst[19:12],12'b0};
$display("AUIPC");
imm_en <= 1'b1;
end
//B-type
7'b11000_11 : begin
imm <= {sign,inst[7],inst[30:25],inst[11:8],1'b0};
$display("BRANCH");
imm_en <= 1'b1;
end
default: begin
imm <= 32'h00; $display ("default");
imm_en <= 1'b0;
end
endcase
end
endmodule
Тестбэнч:
`include "rv_imm.v"
`timescale 10ns/1ns
/*
input [31:0] inst,
output reg [31:0] imm,
output reg imm_en
*/
module testbench; // input and output test signals
reg [31:0] inst;
wire [31:0] imm;
wire [31:0] imm_en;
// creating the instance of the module we want to test
// bcd_to_sseg - module name
// dut - instance name ('dut' means 'device under test')
rv_imm dut( inst, imm, imm_en );
// do at the beginning of the simulation
initial
begin
inst = 32'h50b7; // set test signals value
#10; // pause
inst = 32'h0137; // set test signals value
#10; // pause
inst = 32'h508193; // set test signals value
#10; // pause
inst = 32'h502083; // set test signals value
#10; // pause
inst = 32'hfe000ce3; // set test signals value
#10; // pause
inst = 32'h77237; // set test signals value
#10; // pause
inst = 32'hc0d093; // set test signals value
#10; // pause
inst = 32'hff9ff2ef; // set test signals value
#10; // pause
end // do at the beginning of the simulation
// print signal values on every change
initial
$monitor("inst=%h imm=%h", inst, imm, imm_en);
// do at the beginning of the simulation
initial
$dumpvars; //iverilog dump init
endmodule
Для проверки в симуляторе введем небольшую программу, а если точнее, то небольшой список инструкций разных типов для тестирования реакции модуля формирования непосредственных значений.