summaryrefslogtreecommitdiff
path: root/rtl/cache
diff options
context:
space:
mode:
Diffstat (limited to 'rtl/cache')
-rw-r--r--rtl/cache/cache_control.sv13
-rw-r--r--rtl/cache/offsets.sv12
-rw-r--r--rtl/cache/routing.sv40
-rw-r--r--rtl/cache/sram.sv8
4 files changed, 64 insertions, 9 deletions
diff --git a/rtl/cache/cache_control.sv b/rtl/cache/cache_control.sv
index 46d4638..33d380a 100644
--- a/rtl/cache/cache_control.sv
+++ b/rtl/cache/cache_control.sv
@@ -28,6 +28,7 @@ module cache_control
output ring_token out_token,
output logic out_token_valid,
+ // Señales para la SRAM
input addr_tag tag_rd,
input line data_rd,
input line_state state_rd,
@@ -88,9 +89,11 @@ module cache_control
*/
assign unlock_line = !core_waitrequest;
+ // Replace si no coinciden las tags y el estado no es INVALID
assign replace = tag_rd != core_tag && state_rd != INVALID;
- assign last_hop = in_hold.ttl == `TTL_END;
- assign snoop_hit = tag_rd == in_hold.tag;
+ assign last_hop = in_hold.ttl == `TTL_END; //Indica si es el último salto
+ assign snoop_hit = tag_rd == in_hold.tag; //Snoop hit si coinciden las tags
+ // Aceptar snoop si no es el último nodo y se tiene un mensaje válido
assign accept_snoop = in_hold_valid && !last_hop && (in_hold.inval || !in_hold.reply);
assign may_send = may_send_if_token_held && in_token_valid;
@@ -104,7 +107,7 @@ module cache_control
assign out_data_valid = out_stall || send || (in_hold_valid && !last_hop && in_data_ready);
assign send_data.tag = core_tag;
- assign send_data.ttl = `TTL_MAX;
+ assign send_data.ttl = `TTL_MAX; // Acá se inicializa el valor máximo de TTL
assign send_data.data = fwd_data.data; // Esto evita muchos muxes
assign send_data.read = send_read;
assign send_data.index = core_index;
@@ -139,11 +142,13 @@ module cache_control
unique case (state)
ACCEPT: begin
+ // Si es el último nodo en recibir el mensaje y la request no es de lectura
if (last_hop && !in_hold.read) begin
- end_reply = in_hold_valid;
+ end_reply = in_hold_valid; // Se termina el paso de ese mensaje
in_data_ready = 1;
end
+ // Si no es el último salto y hay reply
if (!last_hop && in_hold.reply && !in_hold.inval)
in_data_ready = 1;
diff --git a/rtl/cache/offsets.sv b/rtl/cache/offsets.sv
index a933d1c..205d847 100644
--- a/rtl/cache/offsets.sv
+++ b/rtl/cache/offsets.sv
@@ -2,7 +2,7 @@
module cache_offsets
(
- input addr_offset core_offset,
+ input addr_offset core_offset, // El offset es un input pero no un output porque se mapea
input word_be core_byteenable,
input word core_writedata,
input line core_readdata_line,
@@ -10,21 +10,24 @@ module cache_offsets
output line core_data_wr,
core_writedata_line,
- output word core_readdata,
+ output word core_readdata, // Readdata pasa de ser una line en el input a una word por el offset
output line_be core_byteenable_line
);
-
+ // Simplificar offset, para que sea transparente para la cache
line line_mask;
+
+ // El byteenable se utiliza para leer o escribir en cache algo diferente a una word
word be_extend, mask3, mask2, mask1, mask0;
word_be be3, be2, be1, be0;
assign core_writedata_line = {4{core_writedata}};
assign core_byteenable_line = {be3, be2, be1, be0};
+ // Concatenar para extender a una word ([31:0]) según el valor de byteenable que es [3:0]
assign be_extend = {{8{core_byteenable[3]}}, {8{core_byteenable[2]}},
{8{core_byteenable[1]}}, {8{core_byteenable[0]}}};
- assign line_mask = {mask3, mask2, mask1, mask0};
+ assign line_mask = {mask3, mask2, mask1, mask0}; // Máscara para toda la línea
assign core_data_wr = (core_writedata_line & line_mask) | (data_rd & ~line_mask);
always_comb begin
@@ -38,6 +41,7 @@ module cache_offsets
be1 = 0;
be0 = 0;
+ // Elegir la word que se va a retornar según el valor de offset
unique case (core_offset)
2'b00: begin
be0 = core_byteenable;
diff --git a/rtl/cache/routing.sv b/rtl/cache/routing.sv
index c72d9b5..78f1be0 100644
--- a/rtl/cache/routing.sv
+++ b/rtl/cache/routing.sv
@@ -37,6 +37,12 @@ module cache_routing
output line_be mem_byteenable
);
+ /* Módulo para enrutar las operaciones a cache o memoria
+ * Esto porque hay escrituras que definitivamente no pueden quedar en cache
+ * como el caso de periféricos, para los cuales si se guarda "su valor" en
+ * cache y no en memoria se harían lecturas incorrectas
+ */
+
word core_address_line;
logic cached, cache_mem, transition;
addr_io_region io;
@@ -48,29 +54,56 @@ module cache_routing
BYPASS
} state;
+ //Arbitrar el bus del lado de la cache
+
+ /* Se sabe si el address es cache o no evaluando los bits de IO.
+ * Esto es posible porque se cumple lo siguiente:
+ * - La memoria tiene un tamaño que es una potencia de 2
+ * - Sus direcciones inician en 0
+ * Entonces si los bits de IO son distintos de 0, se sabe que no es
+ * una dirección cached
+ */
assign cached = io == 3'b000;
+ // Se afirma si cache quiere hacer un read o write de memoria
assign cache_mem = cache_mem_read || cache_mem_write;
+ // Acá se divide el core_address para analizarse por separado
assign {io, core_tag, core_index, core_offset} = core_address;
assign core_address_line = {io, core_tag, core_index, 4'b0000};
+ // Si está cached se asigna a lectura de cache, sino a lectura de memoria
assign core_readdata_line = cached ? data_rd : mem_readdata;
+ // Se afirma si el core quiere leer/escribir a cache y efectivamente es una
+ // dirección de cache
assign cache_core_read = core_read && cached;
assign cache_core_write = core_write && cached;
+ // Máquina de estados:
+ // IDLE/CACHE/BYPASS
+ // Bypass: el request evita pasar por caché, para que no quede escrito el
+ // el dato. Esto sirve para periféricos, por ejemplo.
+ // Cache: el request sí pasa por caché, esto sucede para todo lo que va
+ // para RAM.
always_comb begin
transition = 0;
core_waitrequest = cache_core_waitrequest;
+ // Desde el punto de vista de cache, mem le hace waitreq a cache
cache_mem_waitrequest = 1;
unique case (state)
IDLE:
+ /* Transition se afirma si cache quiere hacer un read o write de
+ * memoria, o si el address no es cache y el core quiere leer
+ * o escribir a cache
+ */
transition = cache_mem || (!cached && (core_read || core_write));
CACHE:
+ // Cache le hace waitreq a memoria
cache_mem_waitrequest = mem_waitrequest;
BYPASS:
+ // Se le hace waitreq al core si la memoria también lo hace
core_waitrequest = mem_waitrequest;
endcase
end
@@ -83,6 +116,11 @@ module cache_routing
end else unique case (state)
IDLE:
if (transition) begin
+ // Si cache quiere hacer una operación con memoria, se pasa
+ // a CACHE, sino hay que hacer BYPASS. Si la operación
+ // no es directo a memoria, se hace bypasss porque esto
+ // implica que el dato no tiene que quedar cacheado. (Talvez
+ // es algo de un periférico, etc.)
state <= cache_mem ? CACHE : BYPASS;
mem_read <= cache_mem ? cache_mem_read : core_read;
mem_write <= cache_mem ? cache_mem_write : core_write;
@@ -98,6 +136,8 @@ module cache_routing
always_ff @(posedge clk)
if (transition) begin
+ // Si cache no quiere hacer una operación con memoria, se asignan
+ // las señales del core
mem_address <= cache_mem ? cache_mem_address : core_address_line;
mem_writedata <= cache_mem ? cache_mem_writedata : core_writedata_line;
mem_byteenable <= cache_mem ? 16'hffff : core_byteenable_line;
diff --git a/rtl/cache/sram.sv b/rtl/cache/sram.sv
index 986c09b..74f9e65 100644
--- a/rtl/cache/sram.sv
+++ b/rtl/cache/sram.sv
@@ -26,7 +26,12 @@ module cache_sram
line data_file[DEPTH] /*verilator public*/;
addr_tag tag_file[DEPTH] /*verilator public*/;
line_state state_file[DEPTH] /*verilator public*/;
-
+
+ /* 3 funciones principales:
+ * 1. Si se necesita escribir un dato: escribe en los tag y data files en la posición del index de escritura
+ * 2. Si se necesita escribir un estado: escribe en el state file en la posición del index de escritura
+ * 3. Cada ciclo retorna siempre lo que esté en todos los files en la posición de index de lectura
+ */
always_ff @(posedge clk) begin
if (write_data) begin
tag_file[index_wr] <= tag_wr;
@@ -41,6 +46,7 @@ module cache_sram
state_rd <= state_file[index_rd];
end
+ // Se inicializan todas las líneas del state file como INVALID
//FIXME: rst_n para state_file?
initial
for (int i = 0; i < DEPTH; ++i)