-
Notifications
You must be signed in to change notification settings - Fork 0
Controlador de Operações
[English]
O Controlador de Operações é o componente responsável por gerenciar todos os outros. Ele recebe os dados de instrução da RAM, que são então decodificados e usados para enviar sinais por fios de controle específicos.
O Controlador de Operações possui apenas algumas entradas além de clock, reset e fase. Elas incluem o Barramento de Dados de E/S, o CNZV e três sinais do Controlador de CSR: Exceção, Salto de Sistema e Próxima Carga de Sistema. Três de suas saídas estão conectadas diretamente aos dispositivos de E/S: os sinais de leitura/escrita e o tamanho dos dados.
Internamente, o Controlador de Operações pode ser dividido em várias partes. No início, há um registrador responsável por armazenar a próxima instrução durante a fase de Busca de Instrução (Fase 1). Essa instrução é então decodificada em duas etapas principais durante a Fase 2.
A primeira etapa extrai parâmetros da instrução, decodifica o opcode e determina o valor imediato. Assim que algumas informações decodificadas estão prontas, elas são armazenadas em registradores específicos. Esses registradores podem enviar sinais diretamente a um componente ou serem combinados com outros sinais, geralmente com a fase. Registradores relacionados a instruções de carga estão conectados a um conjunto adicional de registradores que armazenam valores da "última instrução". Essa configuração permite que o terceiro ciclo do pipeline seja executado corretamente após uma instrução de carga.
A segunda etapa da decodificação de instrução foca na combinação de sinais de diferentes instruções que ativam os mesmos componentes. Alguns sinais merecem destaque:
Logo abaixo do "Contador de Programa", há lógica de controle para todos os saltos possíveis que o sistema pode executar. Instruções de salto incondicional são decodificadas durante a fase ID do pipeline. No entanto, para instruções de desvio condicional, o sinal de salto é decodificado durante a fase EX devido à sua dependência do valor CNZV. O CNZV recebido da ULA é decodificado pelo Controlador de Operações em várias condições como ==, !=, <, >= e outras, antes de ser multiplexado no sinal de salto.
A função Op sob "ULA e Deslocador" utiliza subtração para qualquer operação que não seja OP ou OP-IMM. Isso pode parecer incorreto — especialmente porque AUIPC não deveria envolver subtração — mas é importante notar que quando a ULA usa um valor imediato, ela considera apenas os três primeiros bits da operação. Isso significa que uma operação de subtração (1000) é interpretada como uma adição (000).
Alguns sinais são combinados com o sinal de exceção do Controlador de CSR. Isso evita que instruções corrompidas apliquem mudanças no estado da máquina, prevenindo danos adicionais.
Qualquer instrução de salto gera um sinal de descarte de instrução que força a próxima instrução a ser um NOP.
Por fim, podemos analisar brevemente os dois decodificadores presentes no Controlador. O Decodificador de Operações é construído usando um componente PLA, que significa Matriz Lógica Programável. Trata-se essencialmente de uma matriz de portas AND conectadas a portas OR, permitindo qualquer tradução possível de um conjunto de bits para outro. Na figura a seguir, você pode ver como ele é configurado. Note que qualquer combinação não presente resulta em uma saída zerada.
O Decodificador de Imediato, por outro lado, consiste em conexões simples de fios, circuitos de extensão de sinal e multiplexadores para selecionar o tipo de valor imediato.
`timescale 1s/1s
//Entradas e Saídas
module operation_controller(
input clock,
input reset,
input [2:1] phase,
input [31:0] data_in,
output reg [31:0] immediate,
/////Controlador CSR
output [31:0] next_instruction,
output reg [31:0] current_instruction,
output [9:0] next_decoded_instruction,
output reg store,
output reg load,
output jump,
input exception,
input system_jump,
input next_system_load,
/////Miscelanea/////
output reg load_upper_immediate,
/////Arquivo de Registradores/////
output [14:0] registers_addresses,
output register_file_write,
/////ULA/////
input [3:0] cnzv,
output reg [4:0] op_function,
output reg alu_use_pc,
output reg use_immediate,
/////Contador de Programa/////
output pc_read_next,
output reg pc_relative,
output pc_use_offset,
output forward_address,
/////Buffer de Entrada/////
output reg [2:0] data_type,
output input_buffer_write,
output input_buffer_read,
/////Interface de Saída/////
output pad_read,
output pad_write,
output [1:0]pad_data_size
);
// Decodificador de Instruções
reg [31:0] next_instruction_reg;
assign next_instruction = flush ? 32'h00000013 : next_instruction_reg ;
//Aqui podemos comparar diretamente o valor hexadecimal com o opcode das instruções
wire next_immediate = next_instruction[6:0] == 7'h13;
wire next_load = next_instruction[6:0] == 7'h03 | next_system_load;
wire next_add_upper_immediate_pc = next_instruction[6:0] == 7'h17;
wire next_store = next_instruction[6:0] == 7'h23;
wire next_operation = next_instruction[6:0] == 7'h33 | next_immediate;
wire next_load_upper_immediate = next_instruction[6:0] == 7'h37;
wire next_branch = next_instruction[6:0] == 7'h63;
wire next_jump_and_link_register = next_instruction[6:0] == 7'h67;
wire next_jump_and_link = next_instruction[6:0] == 7'h6f;
wire next_system = next_instruction[6:0] == 7'h73;
assign next_decoded_instruction = {
next_system, next_jump_and_link, next_jump_and_link_register, next_branch, next_load_upper_immediate,
next_operation, next_store, next_add_upper_immediate_pc, next_immediate, next_load
};
// Endereços do arquivo de registradores
assign registers_addresses = {
current_instruction[24:15], last_load & phase[1] ? last_c_address : current_instruction[11:7]
};
assign register_file_write = phase[2] & write_c & !exception | phase[1] & last_load;
// número de função das instruções
wire [6:0] next_funct_7 = next_instruction[31:25];
wire [2:0] next_funct_3 = next_instruction[14:12];
wire valid_store = store & !exception;
wire valid_load = load & !exception;
///Registradores
reg [4:0] last_c_address;
reg write_c;
reg unconditional_jump;
reg branch;
reg last_load;
//********************************************************************************************************************//
// Contador de programa
assign pc_read_next = phase[2] & unconditional_jump;
wire decoded_cnzv =
current_instruction[14:12] == 3'h0 ? cnzv[2] :
current_instruction[14:12] == 3'h1 ? ~cnzv[2] :
current_instruction[14:12] == 3'h4 ? (cnzv[1] ^ cnzv[3]) :
current_instruction[14:12] == 3'h5 ? ~(cnzv[1] ^ cnzv[3]) :
current_instruction[14:12] == 3'h6 ? cnzv[0] :
current_instruction[14:12] == 3'h7 ? ~cnzv[0] : 1'b0;
assign jump = unconditional_jump | (branch & decoded_cnzv);
wire flush = jump | system_jump;
assign pc_use_offset = valid_store;
assign forward_address = phase[2] & (valid_load | valid_store) ;
// Entrada/Saída e Pads de interface
assign input_buffer_write = phase[2] & valid_load;
assign input_buffer_read = phase[1] & last_load;
assign pad_read = phase[1] | input_buffer_write;
assign pad_write = phase[2] & valid_store;
assign pad_data_size = {current_instruction[13], current_instruction[13] | current_instruction[12]};
//********************************************************************************************************************//
// Atualização dos Registradores
always @(posedge clock) begin
if (reset) begin
next_instruction_reg <= 32'h00000013;
current_instruction <= 32'h00000013;
unconditional_jump <= 0;
pc_relative <= 0;
branch <= 0;
use_immediate <= 0;
op_function <= 5'b0;
alu_use_pc <= 0;
load_upper_immediate <= 0;
store <= 0;
load <= 0;
write_c <= 0;
immediate <= 32'b0;
last_load <= 0;
last_c_address <= 5'b0;
data_type <= 3'b0;
end
else if (phase[1]) begin
next_instruction_reg <= data_in;
end
else if (phase[2]) begin
current_instruction <= next_instruction;
unconditional_jump <= next_jump_and_link | next_jump_and_link_register;
pc_relative <= next_branch | next_jump_and_link;
branch <= next_branch;
use_immediate <= next_add_upper_immediate_pc | next_immediate;
op_function <= next_operation ? {next_funct_7[5], next_funct_7[1], next_funct_3} : 5'h10;
alu_use_pc <= next_add_upper_immediate_pc;
load_upper_immediate <= next_load_upper_immediate;
store <= next_store;
load <= next_load;
write_c <= (next_system | next_jump_and_link_register | next_jump_and_link |
next_operation | next_add_upper_immediate_pc | next_load_upper_immediate);
immediate <=
next_add_upper_immediate_pc | next_load_upper_immediate ? {next_instruction[31:12], 12'b0} :
next_jump_and_link ? {{12{next_instruction[31]}}, next_instruction[19:12], next_instruction[20], next_instruction[30:21], 1'b0} :
next_branch ? {{20{next_instruction[31]}}, next_instruction[7], next_instruction[30:25], next_instruction[11:8], 1'b0} :
next_store ? {{20{next_instruction[31]}}, next_instruction[31:25], next_instruction[11:7]} :
{{20{next_instruction[31]}}, next_instruction[31:20]};
last_load <= valid_load;
last_c_address <= current_instruction[11:7];
data_type <= current_instruction[14:12];
end
end
endmodule-
- 1.1 Introduction
- 1.2 RISC-V Implementation
- 1.2.1 Available Instruction Set
- 1.2.2 Available Non-ISA Features
-
- 2.1 ALU
- 2.2 Register File
- 2.3 Program Counter
- 2.4 Input Buffer
- 2.5 RAM
- 2.6 Operation Controller
- 2.7 CSR Controller
-
- 3.1 Input Devices
- 3.1.1 Keyboard
- 3.1.2 Switches and Joystick
- 3.1.3 Random Number Generator
- 3.1.4 Real-Time Device
- 3.2 Output Devices
- 3.2.1 Screen
- 3.2.2 Terminal
- 3.2.3 Software Interrupt Register
- 3.1 Input Devices