Опубликован: 19.04.2025 | Доступ: свободный | Студентов: 1 / 0 | Длительность: 07:05:00
Лекция 5:

Построение однотактного простейшего ядра RISC-V

< Лекция 4 || Лекция 5: 12345 || Лекция 6 >

Введение

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

Вернемся к основным архитектурным блокам процессора:

  • регистровый файл/файл-регистр (31 активный регистр, плюс "регистр ноля", два выходных порта, один порт на запись);
  • программный счётчик (PC);
  • блок регистров специального назначения (CSR);
  • арифметико-логическое устройство (АЛУ);
  • блок дешифратора команд.

Дополнительными блоками не входящими в архитектурные блоки процессорного ядра, но необходимые для работы:

  • память программ;
  • память данных (оперативная память).

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

Файл-регистр

Файл-регистр данных архитектур устроен довольно просто - особенно для базовых конфигураций архитектур.

Тридцать два - 32-разрядных регистра, два порта на чтение данных (для упрощения делаем их асинхронными), один синхронный порт на запись данных. Регистр файла с адресом 0 - всегда при чтении выдает ноль, на запись не работает.

Файл-регистр принимает адреса регистров-операндов (rs1, rs2), асинхронно выдает содержимое соответствующих регистров на выходные порты. Параллельно при наличии разрешающего сигнала записывает в регистр с адресом rd значение, поданное на вход.

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

Код файл-регистра:

module rv_reg_file
#(
  parameter DATA_WIDTH=32, 
  parameter ADDR_WIDTH=5
  )
( input clk,
  input [(ADDR_WIDTH-1):0] rs1,
  input [(ADDR_WIDTH-1):0] rs2,
  input [(ADDR_WIDTH-1):0] rd,

  output reg [(DATA_WIDTH-1):0] Rs1_out,
  output reg [(DATA_WIDTH-1):0] Rs2_out,
  input [(DATA_WIDTH-1):0] Rd_input,

  input we
);
// RAM array
  reg [DATA_WIDTH-1:0] ram[0:2**ADDR_WIDTH-1];
  wire rd_nonzero;
  wire rs1_nonzero;
  wire rs2_nonzero;

  assign rd_nonzero = |rd;
  assign rs1_nonzero = |rs1;
  assign rs2_nonzero = |rs2;
// read RAM content from file
//initial
//$readmemh("ram.txt",ram);

always @ (posedge clk)
  begin
    if (we & rd_nonzero) ram[rd] <= Rd_input;
  end

always @ (*)
  begin
    Rs1_out <= rs1_nonzero ? ram[rs1] : 32'h0;
    Rs2_out <= rs2_nonzero ? ram[rs2] : 32'h0;
  end
// */
endmodule

Тест-бенч:

`include "rv_reg_file.v"
`timescale 10ns/1ns
/*
 input clk,
  input [(ADDR_WIDTH-1):0] rs1,
  input [(ADDR_WIDTH-1):0] rs2,
  input [(ADDR_WIDTH-1):0] rd,
  output reg [(DATA_WIDTH-1):0] Rs1_out,
  output reg [(DATA_WIDTH-1):0] Rs2_out,
  input [(DATA_WIDTH-1):0] Rd_input,
  input we
*/
module testbench; // input and output test signals
reg clk;
reg [4:0] rs1;
reg [4:0] rs2;
reg [4:0] rd;
wire [31:0] Rs1_out;
wire [31:0] Rs2_out;
reg [31:0] Rd_input;
reg we;

rv_reg_file dut ( clk, rs1, rs2, rd, Rs1_out, Rs2_out, Rd_input, we);    
integer i;
initial
begin
   clk = 1'b0; rs1 = 5'h00; rs2 = 5'h01; rd = 5'h02; we=1'b0;
   #10;
   i = 0;
   rs1 <= #i 5'h00; rs2 <= #i 5'h01; rd <= #i 5'h02; Rd_input <= #i 32'h01; we <= #i 1'b0; i = i + 20;
   rs1 <= #i 5'h01; rs2 <= #i 5'h01; rd <= #i 5'h01; Rd_input <= #i 32'h77; we <= #i 1'b0; i = i + 20; //we = 1'b1;
   rs1 <= #i 5'h02; rs2 <= #i 5'h01; rd <= #i 5'h02; Rd_input <= #i 32'h01; we <= #i 1'b0; i = i + 20; //we = 1'b1;
   rs1 <= #i 5'h03; rs2 <= #i 5'h02; rd <= #i 5'h03; Rd_input <= #i 32'h01; we <= #i 1'b0; i = i + 20;
   rs1 <= #i 5'h04; rs2 <= #i 5'h04; rd <= #i 5'h04; Rd_input <= #i 32'h01; we <= #i 1'b1; i = i + 20; 
   rs1 <= #i 5'h05; rs2 <= #i 5'h04; rd <= #i 5'h05; Rd_input <= #i 32'h01; we <= #i 1'b0; i = i + 20; //we = 1'b0;
   rs1 <= #i 5'h00; rs2 <= #i 5'h01; rd <= #i 5'h06; Rd_input <= #i 32'h01; we <= #i 1'b0; i = i + 20; 
   rs1 <= #i 5'h00; rs2 <= #i 5'h01; rd <= #i 5'h07; Rd_input <= #i 32'h01; we <= #i 1'b0; i = i + 20;
   for(i=0; i < 80; i = i+1)
      #10 clk = ~clk;
end

initial         
  $monitor("Rs1=%h Rs2=%h ",
            Rs2_out, Rs2_out);    
initial         
  $dumpvars;  //iverilog dump init
endmodule
Временные диаграммы тестирования файл-регистра

Рис. 5.1. Временные диаграммы тестирования файл-регистра

АЛУ

Арифметико-логическое устройство является одним из центральных узлов любого процессора. Структура АЛУ и его функциональность определяется ISA, поэтому вернемся к еще раз к базовым операциям RISC-V.

Операции с регистрами и константами (immediate/непосредственными значениями) - тип-I - оперируют с регистрами и непосредственными операндами - арифметико-логические операции, сравнения с константами, загрузкой данных в регистры

funct3 opcode I-type
Rd = Rs1+imm (NOP encoded as ADDI x0, x0, 0.) 000 0010011 ADDI
Rd= 1 if Rs< imm else 0 010 0010011 SLTI
Rd= 1 if Rs< uimm else 0 011 0010011 SLTIU
Rd = rs1 XOR imm 100 0010011 XORI
Rd = rs1 OR imm 110 0010011 ORI
Rd = rs1 AND imm 111 0010011 ANDI
Загрузка в регистры непосредственных значения (immediate)
opcode U-type
Rd = imm[31:12][0_{12}] 0110111 LUI
Rd=PC+ imm[31:12] [0_{12}] 0010111 AUIPC
Группа регистровых операций (R-типа)
funct7 funct3 opcode R-type
Rd = Rs1 + Rs2 0000000 000 0110011 ADD
Rd = Rs1 - Rs2 0100000 000 0110011 SUB
Rd = Rs1 << Rs2 0000000 001 0110011 SLL
Rd= 1 if Rs1 < Rs2 else 0 0000000 010 0110011 SLT
Rd= 1 if Rs1 < Rs2 else 0(unsign)
SLTU rd, x0, rs2 sets rd to 1
if rs2 is not equal to zero
0000000 011 0110011 SLTU
Rd = Rs1 XOR Rs2 0000000 100 0110011 XOR
Rd = Rs1 >> Rs2 0000000 101 0110011 SRL
Rd = Rs1 >> Rs2 (sign ext) 0100000 101 0110011 SRA
Rd = Rs1 OR Rs2 0000000 110 0110011 OR
Rd = Rs1 AND Rs2 0000000 111 0110011 AND
Rd = Rs1 << imm 0000000 001 0010011 SLLI
Rd = Rs1 >> imm (0 ext) 0000000 101 0010011 SRLI
Rd = Rs1 >> imm (sign ext) 0100000 101 0010011 SRAI
< Лекция 4 || Лекция 5: 12345 || Лекция 6 >