Implementación de Multihilo en Hardware para un Procesador Soft-Core RISC-V en FPGA
Una microarquitectura multihilo para un procesador soft-core basado en RISC-V utiliza registros sombra para almacenar los estados de los harts, permitiendo un cambio de contexto rápido sin intervención del software. Esto simplifica el manejo de eventos asíncronos en proyectos de FPGA, minimizando la carga del controlador de interrupciones y reduciendo el volumen de código. La implementación está dirigida a sistemas con un único nivel de privilegio y espacio de direcciones compartido.
Fundamentos de la Microarquitectura
El elemento clave son los arreglos de registros sombra para cada hart (usando la terminología de RISC-V). Cada hart guarda su estado en copias de los registros x0-x31 y el PC. La ALU, la memoria, los periféricos y los registros CSR permanecen compartidos. La protección de datos se implementa a nivel de software.
Se admite el conjunto de instrucciones base I+Zicsr. La gestión de hilos se maneja a través de registros CSR y una tabla de harts (hart_table). El cambio de hart ocurre cíclicamente basado en el temporizador del sistema, usando sus bits menos significativos para direccionar la tabla.
Los bits de actividad del hart controlan las escrituras al PC, los registros y la memoria, y reinician la tubería de la ALU para los hilos inactivos.
Módulo de Registros CSR
El bloque CSR se divide en puertos de lectura y escritura independientes para compatibilidad con multihilo:
module rv_csr
#(
parameter DATA_WIDTH=32,
parameter ADDR_WIDTH=12,
parameter CSR_size = 32
)
(input clk,
input [(ADDR_WIDTH-1):0] csr_addr_in,
input [(DATA_WIDTH-1):0] csr_in,
input [(ADDR_WIDTH-1):0] csr_addr_out,
output reg [(DATA_WIDTH-1):0] csr_out,
input csr_wr,
input en
);
// csr register file
reg [ADDR_WIDTH-1:0] csr_reg[0:CSR_size-1];
always @ (posedge clk)
begin
case (csr_addr_in)
32'h0 : begin
if (csr_wr&en) begin
csr_reg[0] <= csr_in;
end
end
default: begin
csr_reg[1] <= 32'h555;
end
endcase
case (csr_addr_out)
32'h0 : begin
csr_out <= csr_reg[0];
end
default: begin
csr_out<=32'hAAA;
end
endcase
end
endmodule
Archivo de Registros con Soporte para Harts
El archivo de registros multihilo implementa un arreglo RAM para 8 harts. Las operaciones de lectura/escritura se direccionan mediante los números hart_in/hart_out:
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 [2:0] hart_in, // hart to read out data from
input [2:0] hart_out, // hart to write data to
input we,
input en,
output reg [(DATA_WIDTH-1):0] x1_out,
output reg [(DATA_WIDTH-1):0] x2_out,
output reg [(DATA_WIDTH-1):0] x3_out,
output reg [(DATA_WIDTH-1):0] x4_out,
output reg [(DATA_WIDTH-1):0] x5_out
);
// RAM array
reg [DATA_WIDTH-1:0] ram[0:2**ADDR_WIDTH-1][0:7];
wire rd_nonzero;
wire rs1_nonzero;
wire rs2_nonzero;
assign rd_nonzero = |rd;
assign rs1_nonzero = |rs1;
assign rs2_nonzero = |rs2;
always @ (posedge clk)
begin
if (en & we & rd_nonzero) ram[rd][hart_in] <= Rd_input;
end
always @ (*)
begin
Rs1_out <= rs1_nonzero ? ram[rs1][hart_out] : 32'h0;
Rs2_out <= rs2_nonzero ? ram[rs2][hart_out] : 32'h0;
x1_out <= ram[1][hart_out];
x2_out <= ram[2][hart_out];
x3_out <= ram[3][hart_out];
x4_out <= ram[4][hart_out];
x5_out <= ram[5][hart_out];
end
// */
endmodule
Contador de Programa para Hilos
El PC se implementa como un arreglo de registros inicializado desde un archivo pc.txt. Las operaciones de incremento y carga ocurren solo para el hart activo:
module rv_pc
#(
parameter WIDTH=32
)
(
input clk,
input rst_n,
input en,
input pc_load,
input [WIDTH-1:0] pc_next,
output [WIDTH-1:0] pc,
output reg [WIDTH-1:0] pc_plus,
input [2:0] hart_in,
input [2:0] hart_out
);
reg [WIDTH-1:0] pc_reg[0:7];
// read initial PCs data values
initial
$readmemh("pc.txt",pc_reg);
always@(posedge clk or negedge rst_n)
begin
if (en) begin
if(pc_load==1'b1)
pc_reg[hart_in] <= pc_next;
else
pc_reg[hart_in] <= pc_reg[hart_in] + 3'h4;
end
end
assign pc = pc_reg[hart_out];
always @ *
begin
pc_plus <= pc_reg[hart_out] + 3'h4;
end
endmodule
Tabla de Harts y Gestión
La hart_table es una ROM con 8 entradas (4 bits: 1 bit de actividad + 3 bits de número de hart), direccionada por el temporizador. El temporizador de sistema de 64 bits sirve tanto como contador cíclico como cronómetro del sistema.
Lista completa de módulos:
- rv_pc — contador de programa multihilo
- rv_mem — memoria unificada
- rv_desh — decodificador con reinicio de tubería
- rv_reg_file — registros sombra
- rv_alu_v — ALU segmentada
- rv_csr — registros especiales compartidos
- rv_hart_reg — estado del hart
- rv_timer — temporizador de 64 bits
- rv_hart_table — configuración de hilos
Ventajas de la Implementación
- Cambio Rápido: basado en hardware, sin guardar/restaurar registros
- Simplificación de IRQ: hilos en lugar de vectores de interrupción
- Compatibilidad con RISC-V: soporte del concepto hart
- Optimización para FPGA: recursos compartidos, lógica mínima
Puntos Clave
- Los registros sombra almacenan el estado arquitectónico completo de cada hart
- Los CSR compartidos requieren puertos R/W separados para funcionar correctamente
- La actividad del hart controla todas las escrituras y reinicia la tubería
- El temporizador direcciona la hart_table cíclicamente, asegurando planificación round-robin
- La protección de memoria y la sincronización son responsabilidad del SO/RTOS
— Editorial Team
Aún no hay comentarios.