From f3b18ead59ae02f95dabbf0a1dea40873a816975 Mon Sep 17 00:00:00 2001 From: Alejandro Soto Date: Sun, 21 Jan 2024 06:23:46 -0600 Subject: rtl: refactor filenames and directory hierarchy --- rtl/cache/cache_mem.sv | 59 ++++++++++++++++++ rtl/cache/cache_monitor.sv | 113 ++++++++++++++++++++++++++++++++++ rtl/cache/cache_offsets.sv | 91 ++++++++++++++++++++++++++++ rtl/cache/cache_ring.sv | 77 ++++++++++++++++++++++++ rtl/cache/cache_routing.sv | 147 +++++++++++++++++++++++++++++++++++++++++++++ rtl/cache/cache_sram.sv | 71 ++++++++++++++++++++++ rtl/cache/cache_token.sv | 57 ++++++++++++++++++ rtl/cache/mem.sv | 59 ------------------ rtl/cache/monitor.sv | 113 ---------------------------------- rtl/cache/offsets.sv | 91 ---------------------------- rtl/cache/ring.sv | 77 ------------------------ rtl/cache/routing.sv | 147 --------------------------------------------- rtl/cache/sram.sv | 71 ---------------------- rtl/cache/token.sv | 57 ------------------ 14 files changed, 615 insertions(+), 615 deletions(-) create mode 100644 rtl/cache/cache_mem.sv create mode 100644 rtl/cache/cache_monitor.sv create mode 100644 rtl/cache/cache_offsets.sv create mode 100644 rtl/cache/cache_ring.sv create mode 100644 rtl/cache/cache_routing.sv create mode 100644 rtl/cache/cache_sram.sv create mode 100644 rtl/cache/cache_token.sv delete mode 100644 rtl/cache/mem.sv delete mode 100644 rtl/cache/monitor.sv delete mode 100644 rtl/cache/offsets.sv delete mode 100644 rtl/cache/ring.sv delete mode 100644 rtl/cache/routing.sv delete mode 100644 rtl/cache/sram.sv delete mode 100644 rtl/cache/token.sv (limited to 'rtl/cache') diff --git a/rtl/cache/cache_mem.sv b/rtl/cache/cache_mem.sv new file mode 100644 index 0000000..a575d0d --- /dev/null +++ b/rtl/cache/cache_mem.sv @@ -0,0 +1,59 @@ +`include "cache/defs.sv" + +module cache_mem +( + input logic clk, + rst_n, + + input addr_tag core_tag, + + // Señales para la SRAM + input addr_tag tag_rd, // valor de la tag de esa línea + input line data_rd, // datos de la línea + + input addr_index index_wr, + + input logic mem_waitrequest, + output word mem_address, + output logic mem_read, + mem_write, + output line mem_writedata, + + input logic mem_begin, + writeback, + output logic mem_end, + mem_read_end, + mem_wait, + output addr_tag mem_tag, + output addr_index mem_index +); + + assign mem_end = (mem_read || mem_write) && !mem_waitrequest; + assign mem_wait = (mem_read || mem_write) && mem_waitrequest; + assign mem_address = {`IO_CACHED, mem_tag, mem_index, 4'b0000}; + assign mem_read_end = mem_read && !mem_waitrequest; + + always_ff @(posedge clk or negedge rst_n) + if (!rst_n) begin + mem_read <= 0; + mem_write <= 0; + end else begin + if (mem_end) begin + mem_read <= 0; + mem_write <= 0; + end + + if (mem_begin) begin + mem_read <= !writeback; + mem_write <= writeback; + end + end + + always_ff @(posedge clk) + if (mem_begin) begin + mem_tag <= writeback ? tag_rd : core_tag; + mem_index <= index_wr; + mem_writedata <= data_rd; + end + +endmodule diff --git a/rtl/cache/cache_monitor.sv b/rtl/cache/cache_monitor.sv new file mode 100644 index 0000000..380019e --- /dev/null +++ b/rtl/cache/cache_monitor.sv @@ -0,0 +1,113 @@ +`include "cache/defs.sv" + +module cache_monitor +( + input logic clk, + rst_n, + + input addr_tag core_tag, + input addr_index core_index, + input addr_offset core_offset, // Alguno de los 4 cores + input logic core_lock, + input word core_writedata, + output logic[1:0] core_response, + + input line data_rd, + + input logic monitor_acquire, + monitor_fail, + monitor_release, + output line monitor_update, + output logic monitor_commit +); + + // Este módulo provee capacidad para spin_locks (básicamente mutexes) para + // proteger una sección de código a través de spin lock/unlock. + // Esto básicamente es la implemenntación de las instrucciones de ARM + // ldrex, strexeq, que originalmente no son parte ARMv4, esto implica + // que este quad-core es un frankenstein entre ARMv4 y alguna versión + // posterior que sí implementa esas instrucciones. + + + line monitor_rd, monitor_wr; + word update_3, update_2, update_1, update_0; + logic dirty, done, hit, known; + addr_tag tag; + addr_index index; + + logic[3:0] mask, mask_clear, core_ex_mask; + + assign monitor_commit = !core_lock || (hit && known && done); + assign monitor_update = {update_3, update_2, update_1, update_0}; + + /* Avalon p. 15: + * - 00: OKAY - Successful response for a transaction. + * - 10: SLVERR - Error from an endpoint agent. Indicates an unsuccessful transaction. + */ + assign core_response = {monitor_fail, 1'b0}; + + assign hit = tag == core_tag && index == core_index; + assign done = monitor_rd == data_rd && mask_clear == 4'b0000; + assign known = mask[core_offset]; + assign mask_clear = mask & ~core_ex_mask; + + always_comb begin + {update_3, update_2, update_1, update_0} = monitor_wr; + + unique case (core_offset) + 2'b00: begin + update_0 = core_writedata; + core_ex_mask = 4'b0001; + end + + 2'b01: begin + update_1 = core_writedata; + core_ex_mask = 4'b0010; + end + + 2'b10: begin + update_2 = core_writedata; + core_ex_mask = 4'b0100; + end + + 2'b11: begin + update_3 = core_writedata; + core_ex_mask = 4'b1000; + end + endcase + end + + always @(posedge clk or negedge rst_n) + if (!rst_n) begin + mask <= 4'b0000; + dirty <= 0; + end else begin + // ldrex + if (monitor_acquire) begin + mask <= hit && !known && !dirty ? mask | core_ex_mask : core_ex_mask; + dirty <= 0; + end + + // strexeq + if (monitor_release) begin + mask <= hit && known ? mask_clear : 4'b0000; + dirty <= hit && known; + end + end + + always_ff @(posedge clk) begin + if (monitor_acquire) begin + tag <= core_tag; + index <= core_index; + + if (!hit || known || dirty || mask == 4'b0000) begin + monitor_rd <= data_rd; + monitor_wr <= data_rd; + end + end + + if (monitor_release) + monitor_wr <= monitor_update; + end + +endmodule diff --git a/rtl/cache/cache_offsets.sv b/rtl/cache/cache_offsets.sv new file mode 100644 index 0000000..7769394 --- /dev/null +++ b/rtl/cache/cache_offsets.sv @@ -0,0 +1,91 @@ +`include "cache/defs.sv" + +module cache_offsets +( + 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, + data_rd, + + output line core_data_wr, + core_writedata_line, + 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 +); + + // Este módulo sirve para simplificar offsets, para que sean transparentes + // para la cache. El caché nunca ve la parte de offset que hay en las + // direccciones. + + // El core trabaja en words. El caché en lines, esto es el puente entre + // ambos tipos de datos. + + line line_mask; + + // El byteenable (be) se utiliza cuando se quiere leer o escribir en cache + // un solo byte, en lugar de una word entera + word be_extend, mask3, mask2, mask1, mask0; + word_be be3, be2, be1, be0; + + // Concatena la misma word 4 veces. + assign core_writedata_line = {4{core_writedata}}; + + // Se prepara la mask de byte enable para cada word. + assign core_byteenable_line = {be3, be2, be1, be0}; + + // Concatenar para extender a una word ([31:0]). El valor de be determina + // a cuál word se va a extender. + assign be_extend = {{8{core_byteenable[3]}}, {8{core_byteenable[2]}}, + {8{core_byteenable[1]}}, {8{core_byteenable[0]}}}; + + // Máscara para toda la línea + assign line_mask = {mask3, mask2, mask1, mask0}; + + // Se preserva lo que no hay que cambiar (data_rd & ~line_mask) y se aplica + // la máscara a lo que sí hay cambiar (core_writedata_line & line_mask). + assign core_data_wr = (core_writedata_line & line_mask) | (data_rd & ~line_mask); + + always_comb begin + mask3 = 0; + mask2 = 0; + mask1 = 0; + mask0 = 0; + + be3 = 0; + be2 = 0; + 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; + mask0 = be_extend; + core_readdata = core_readdata_line[31:0]; + end + + 2'b01: begin + be1 = core_byteenable; + mask1 = be_extend; + core_readdata = core_readdata_line[63:32]; + end + + 2'b10: begin + be2 = core_byteenable; + mask2 = be_extend; + core_readdata = core_readdata_line[95:64]; + end + + 2'b11: begin + be3 = core_byteenable; + mask3 = be_extend; + core_readdata = core_readdata_line[127:96]; + end + endcase + end + +endmodule diff --git a/rtl/cache/cache_ring.sv b/rtl/cache/cache_ring.sv new file mode 100644 index 0000000..d934fb7 --- /dev/null +++ b/rtl/cache/cache_ring.sv @@ -0,0 +1,77 @@ +`include "cache/defs.sv" + +module cache_ring +( + input logic clk, + rst_n, + + input addr_tag core_tag, + input addr_index core_index, + + input ring_req in_data, // lo que se recibe + input logic in_data_valid, // este caché está recibiendo + in_data_ready, + + input logic out_data_ready, // este caché está listo para enviar + output ring_req out_data, // lo que se envía + output logic out_data_valid, // este caché está enviando datos + + input line data_rd, // datos de la línea + + input logic send, + send_read, + send_inval, + set_reply, + output logic out_stall, + in_hold_valid, + last_hop, + output ring_req in_hold +); + + // in_hold: el paquete actual + ring_req send_data, fwd_data, stall_data, out_data_next; + + assign last_hop = in_hold.ttl == `TTL_END; //Indica si es el último salto + + assign out_data = out_stall ? stall_data : out_data_next; + assign out_data_next = send ? send_data : fwd_data; + 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; // 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; + assign send_data.inval = send_inval; + assign send_data.reply = 0; + + always_comb begin + fwd_data = in_hold; + fwd_data.ttl = in_hold.ttl - 2'b1; + + if (set_reply) begin + fwd_data.data = data_rd; + fwd_data.reply = 1; + end + end + + always_ff @(posedge clk or negedge rst_n) + if (!rst_n) begin + in_hold_valid <= 0; + out_stall <= 0; + end else begin + if (in_data_ready) + in_hold_valid <= in_data_valid; + + out_stall <= out_data_valid && !out_data_ready; + end + + always_ff @(posedge clk) begin + if (in_data_ready) + in_hold <= in_data; + + if (!out_stall) + stall_data <= out_data_next; + end + +endmodule diff --git a/rtl/cache/cache_routing.sv b/rtl/cache/cache_routing.sv new file mode 100644 index 0000000..a0c4347 --- /dev/null +++ b/rtl/cache/cache_routing.sv @@ -0,0 +1,147 @@ +`include "cache/defs.sv" +`include "config.sv" + +module cache_routing +( + input logic clk, + rst_n, + + input ptr core_address, + input logic core_read, + core_write, + input line core_writedata_line, + input line_be core_byteenable_line, + output logic core_waitrequest, + output line core_readdata_line, + + output addr_tag core_tag, + output addr_index core_index, + output addr_offset core_offset, + + input line data_rd, + input logic cache_core_waitrequest, + output logic cache_core_read, + cache_core_write, + + input word cache_mem_address, + input logic cache_mem_read, + cache_mem_write, + input line cache_mem_writedata, + output logic cache_mem_waitrequest, + + input logic mem_waitrequest, + input line mem_readdata, + output word mem_address, + output logic mem_read, + mem_write, + output line mem_writedata, + 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; + + enum int unsigned + { + IDLE, + CACHE, + 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 == `IO_CACHED && `CONFIG_CACHE; + // 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 + + always_ff @(posedge clk or negedge rst_n) + if (!rst_n) begin + state <= IDLE; + mem_read <= 0; + mem_write <= 0; + 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; + end + + CACHE, BYPASS: + if (!mem_waitrequest) begin + state <= IDLE; + mem_read <= 0; + mem_write <= 0; + end + endcase + + 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; + end + +endmodule diff --git a/rtl/cache/cache_sram.sv b/rtl/cache/cache_sram.sv new file mode 100644 index 0000000..d63cdad --- /dev/null +++ b/rtl/cache/cache_sram.sv @@ -0,0 +1,71 @@ +`include "cache/defs.sv" + +module cache_sram +( + input logic clk, + rst_n, + + input addr_index index_rd, + index_wr, + input logic write_data, + write_state, + input addr_tag tag_wr, + input line data_wr, + input line_state state_wr, + + output addr_tag tag_rd, + output line data_rd, + output line_state state_rd +); + + // Existe un mito que habla de true dual-ports con byte-enables, + // probablemente no sea real: + // https://www.intel.com/content/www/us/en/docs/programmable/683082/21-3/ram-with-byte-enable-signals.html + + // Es una cache one way: cada índice mapea a cada línea de cache + // (directamente mapeada) + + // Define la cantidad de líneas de cache + // Cantidad de bits en addr_index = 12, entonces se le hace left shift 12 + // espacios a 1. Osea, 4096 líneas de cache. + // Tenemos 4kilo-líneas de caché. Cada línea es de 128bits, osea tenemos una + // caché de 64KBi. + localparam DEPTH = 1 << $bits(addr_index); + + // Estas tres secciones constituyen al caché. + // data_file: Donde se guarda la información cacheada. + // tag_file: Se guardan los tags de las líneas de caché. + // state_file: Se guarda el estado de cada línea de caché. (Acá están todos + // los estados de MESI) + 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; + data_file[index_wr] <= data_wr; + end + + if (write_state) + state_file[index_wr] <= state_wr; + + tag_rd <= tag_file[index_rd]; + data_rd <= data_file[index_rd]; + 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) + state_file[i] = INVALID; + +endmodule diff --git a/rtl/cache/cache_token.sv b/rtl/cache/cache_token.sv new file mode 100644 index 0000000..cb3e59d --- /dev/null +++ b/rtl/cache/cache_token.sv @@ -0,0 +1,57 @@ +`include "cache/defs.sv" + +module cache_token +#(parameter TOKEN_AT_RESET=0) +( + input logic clk, + rst_n, + + input addr_tag core_tag, + input addr_index core_index, + + input ring_token in_token, // input del token + input logic in_token_valid, // se está recibiendo el token + + output ring_token out_token, // output del token + output logic out_token_valid, // se está enviando el token + + input logic send, + lock_line, + unlock_line, + output logic locked, + may_send +); + + logic may_send_if_token_held; + + // Solo se puede iniciar un request si se tiene el token y el token es + // válido + assign may_send = may_send_if_token_held && in_token_valid; + assign may_send_if_token_held + = (!in_token.e2.valid || in_token.e2.index != core_index || in_token.e2.tag != core_tag) + && (!in_token.e1.valid || in_token.e1.index != core_index || in_token.e1.tag != core_tag) + && (!in_token.e0.valid || in_token.e0.index != core_index || in_token.e0.tag != core_tag); + + always_ff @(posedge clk or negedge rst_n) + if (!rst_n) begin + out_token <= {($bits(out_token)){1'b0}}; + out_token_valid <= TOKEN_AT_RESET; + + locked <= 0; + end else begin + out_token.e0.tag <= core_tag; + out_token.e0.index <= core_index; + out_token.e0.valid <= may_send_if_token_held && (send || locked) && !unlock_line; + + out_token.e2 <= in_token.e1; + out_token.e1 <= in_token.e0; + out_token_valid <= in_token_valid; + + if (lock_line) + locked <= 1; + + if (unlock_line) + locked <= 0; + end + +endmodule diff --git a/rtl/cache/mem.sv b/rtl/cache/mem.sv deleted file mode 100644 index a575d0d..0000000 --- a/rtl/cache/mem.sv +++ /dev/null @@ -1,59 +0,0 @@ -`include "cache/defs.sv" - -module cache_mem -( - input logic clk, - rst_n, - - input addr_tag core_tag, - - // Señales para la SRAM - input addr_tag tag_rd, // valor de la tag de esa línea - input line data_rd, // datos de la línea - - input addr_index index_wr, - - input logic mem_waitrequest, - output word mem_address, - output logic mem_read, - mem_write, - output line mem_writedata, - - input logic mem_begin, - writeback, - output logic mem_end, - mem_read_end, - mem_wait, - output addr_tag mem_tag, - output addr_index mem_index -); - - assign mem_end = (mem_read || mem_write) && !mem_waitrequest; - assign mem_wait = (mem_read || mem_write) && mem_waitrequest; - assign mem_address = {`IO_CACHED, mem_tag, mem_index, 4'b0000}; - assign mem_read_end = mem_read && !mem_waitrequest; - - always_ff @(posedge clk or negedge rst_n) - if (!rst_n) begin - mem_read <= 0; - mem_write <= 0; - end else begin - if (mem_end) begin - mem_read <= 0; - mem_write <= 0; - end - - if (mem_begin) begin - mem_read <= !writeback; - mem_write <= writeback; - end - end - - always_ff @(posedge clk) - if (mem_begin) begin - mem_tag <= writeback ? tag_rd : core_tag; - mem_index <= index_wr; - mem_writedata <= data_rd; - end - -endmodule diff --git a/rtl/cache/monitor.sv b/rtl/cache/monitor.sv deleted file mode 100644 index 380019e..0000000 --- a/rtl/cache/monitor.sv +++ /dev/null @@ -1,113 +0,0 @@ -`include "cache/defs.sv" - -module cache_monitor -( - input logic clk, - rst_n, - - input addr_tag core_tag, - input addr_index core_index, - input addr_offset core_offset, // Alguno de los 4 cores - input logic core_lock, - input word core_writedata, - output logic[1:0] core_response, - - input line data_rd, - - input logic monitor_acquire, - monitor_fail, - monitor_release, - output line monitor_update, - output logic monitor_commit -); - - // Este módulo provee capacidad para spin_locks (básicamente mutexes) para - // proteger una sección de código a través de spin lock/unlock. - // Esto básicamente es la implemenntación de las instrucciones de ARM - // ldrex, strexeq, que originalmente no son parte ARMv4, esto implica - // que este quad-core es un frankenstein entre ARMv4 y alguna versión - // posterior que sí implementa esas instrucciones. - - - line monitor_rd, monitor_wr; - word update_3, update_2, update_1, update_0; - logic dirty, done, hit, known; - addr_tag tag; - addr_index index; - - logic[3:0] mask, mask_clear, core_ex_mask; - - assign monitor_commit = !core_lock || (hit && known && done); - assign monitor_update = {update_3, update_2, update_1, update_0}; - - /* Avalon p. 15: - * - 00: OKAY - Successful response for a transaction. - * - 10: SLVERR - Error from an endpoint agent. Indicates an unsuccessful transaction. - */ - assign core_response = {monitor_fail, 1'b0}; - - assign hit = tag == core_tag && index == core_index; - assign done = monitor_rd == data_rd && mask_clear == 4'b0000; - assign known = mask[core_offset]; - assign mask_clear = mask & ~core_ex_mask; - - always_comb begin - {update_3, update_2, update_1, update_0} = monitor_wr; - - unique case (core_offset) - 2'b00: begin - update_0 = core_writedata; - core_ex_mask = 4'b0001; - end - - 2'b01: begin - update_1 = core_writedata; - core_ex_mask = 4'b0010; - end - - 2'b10: begin - update_2 = core_writedata; - core_ex_mask = 4'b0100; - end - - 2'b11: begin - update_3 = core_writedata; - core_ex_mask = 4'b1000; - end - endcase - end - - always @(posedge clk or negedge rst_n) - if (!rst_n) begin - mask <= 4'b0000; - dirty <= 0; - end else begin - // ldrex - if (monitor_acquire) begin - mask <= hit && !known && !dirty ? mask | core_ex_mask : core_ex_mask; - dirty <= 0; - end - - // strexeq - if (monitor_release) begin - mask <= hit && known ? mask_clear : 4'b0000; - dirty <= hit && known; - end - end - - always_ff @(posedge clk) begin - if (monitor_acquire) begin - tag <= core_tag; - index <= core_index; - - if (!hit || known || dirty || mask == 4'b0000) begin - monitor_rd <= data_rd; - monitor_wr <= data_rd; - end - end - - if (monitor_release) - monitor_wr <= monitor_update; - end - -endmodule diff --git a/rtl/cache/offsets.sv b/rtl/cache/offsets.sv deleted file mode 100644 index 7769394..0000000 --- a/rtl/cache/offsets.sv +++ /dev/null @@ -1,91 +0,0 @@ -`include "cache/defs.sv" - -module cache_offsets -( - 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, - data_rd, - - output line core_data_wr, - core_writedata_line, - 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 -); - - // Este módulo sirve para simplificar offsets, para que sean transparentes - // para la cache. El caché nunca ve la parte de offset que hay en las - // direccciones. - - // El core trabaja en words. El caché en lines, esto es el puente entre - // ambos tipos de datos. - - line line_mask; - - // El byteenable (be) se utiliza cuando se quiere leer o escribir en cache - // un solo byte, en lugar de una word entera - word be_extend, mask3, mask2, mask1, mask0; - word_be be3, be2, be1, be0; - - // Concatena la misma word 4 veces. - assign core_writedata_line = {4{core_writedata}}; - - // Se prepara la mask de byte enable para cada word. - assign core_byteenable_line = {be3, be2, be1, be0}; - - // Concatenar para extender a una word ([31:0]). El valor de be determina - // a cuál word se va a extender. - assign be_extend = {{8{core_byteenable[3]}}, {8{core_byteenable[2]}}, - {8{core_byteenable[1]}}, {8{core_byteenable[0]}}}; - - // Máscara para toda la línea - assign line_mask = {mask3, mask2, mask1, mask0}; - - // Se preserva lo que no hay que cambiar (data_rd & ~line_mask) y se aplica - // la máscara a lo que sí hay cambiar (core_writedata_line & line_mask). - assign core_data_wr = (core_writedata_line & line_mask) | (data_rd & ~line_mask); - - always_comb begin - mask3 = 0; - mask2 = 0; - mask1 = 0; - mask0 = 0; - - be3 = 0; - be2 = 0; - 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; - mask0 = be_extend; - core_readdata = core_readdata_line[31:0]; - end - - 2'b01: begin - be1 = core_byteenable; - mask1 = be_extend; - core_readdata = core_readdata_line[63:32]; - end - - 2'b10: begin - be2 = core_byteenable; - mask2 = be_extend; - core_readdata = core_readdata_line[95:64]; - end - - 2'b11: begin - be3 = core_byteenable; - mask3 = be_extend; - core_readdata = core_readdata_line[127:96]; - end - endcase - end - -endmodule diff --git a/rtl/cache/ring.sv b/rtl/cache/ring.sv deleted file mode 100644 index d934fb7..0000000 --- a/rtl/cache/ring.sv +++ /dev/null @@ -1,77 +0,0 @@ -`include "cache/defs.sv" - -module cache_ring -( - input logic clk, - rst_n, - - input addr_tag core_tag, - input addr_index core_index, - - input ring_req in_data, // lo que se recibe - input logic in_data_valid, // este caché está recibiendo - in_data_ready, - - input logic out_data_ready, // este caché está listo para enviar - output ring_req out_data, // lo que se envía - output logic out_data_valid, // este caché está enviando datos - - input line data_rd, // datos de la línea - - input logic send, - send_read, - send_inval, - set_reply, - output logic out_stall, - in_hold_valid, - last_hop, - output ring_req in_hold -); - - // in_hold: el paquete actual - ring_req send_data, fwd_data, stall_data, out_data_next; - - assign last_hop = in_hold.ttl == `TTL_END; //Indica si es el último salto - - assign out_data = out_stall ? stall_data : out_data_next; - assign out_data_next = send ? send_data : fwd_data; - 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; // 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; - assign send_data.inval = send_inval; - assign send_data.reply = 0; - - always_comb begin - fwd_data = in_hold; - fwd_data.ttl = in_hold.ttl - 2'b1; - - if (set_reply) begin - fwd_data.data = data_rd; - fwd_data.reply = 1; - end - end - - always_ff @(posedge clk or negedge rst_n) - if (!rst_n) begin - in_hold_valid <= 0; - out_stall <= 0; - end else begin - if (in_data_ready) - in_hold_valid <= in_data_valid; - - out_stall <= out_data_valid && !out_data_ready; - end - - always_ff @(posedge clk) begin - if (in_data_ready) - in_hold <= in_data; - - if (!out_stall) - stall_data <= out_data_next; - end - -endmodule diff --git a/rtl/cache/routing.sv b/rtl/cache/routing.sv deleted file mode 100644 index a0c4347..0000000 --- a/rtl/cache/routing.sv +++ /dev/null @@ -1,147 +0,0 @@ -`include "cache/defs.sv" -`include "config.sv" - -module cache_routing -( - input logic clk, - rst_n, - - input ptr core_address, - input logic core_read, - core_write, - input line core_writedata_line, - input line_be core_byteenable_line, - output logic core_waitrequest, - output line core_readdata_line, - - output addr_tag core_tag, - output addr_index core_index, - output addr_offset core_offset, - - input line data_rd, - input logic cache_core_waitrequest, - output logic cache_core_read, - cache_core_write, - - input word cache_mem_address, - input logic cache_mem_read, - cache_mem_write, - input line cache_mem_writedata, - output logic cache_mem_waitrequest, - - input logic mem_waitrequest, - input line mem_readdata, - output word mem_address, - output logic mem_read, - mem_write, - output line mem_writedata, - 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; - - enum int unsigned - { - IDLE, - CACHE, - 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 == `IO_CACHED && `CONFIG_CACHE; - // 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 - - always_ff @(posedge clk or negedge rst_n) - if (!rst_n) begin - state <= IDLE; - mem_read <= 0; - mem_write <= 0; - 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; - end - - CACHE, BYPASS: - if (!mem_waitrequest) begin - state <= IDLE; - mem_read <= 0; - mem_write <= 0; - end - endcase - - 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; - end - -endmodule diff --git a/rtl/cache/sram.sv b/rtl/cache/sram.sv deleted file mode 100644 index d63cdad..0000000 --- a/rtl/cache/sram.sv +++ /dev/null @@ -1,71 +0,0 @@ -`include "cache/defs.sv" - -module cache_sram -( - input logic clk, - rst_n, - - input addr_index index_rd, - index_wr, - input logic write_data, - write_state, - input addr_tag tag_wr, - input line data_wr, - input line_state state_wr, - - output addr_tag tag_rd, - output line data_rd, - output line_state state_rd -); - - // Existe un mito que habla de true dual-ports con byte-enables, - // probablemente no sea real: - // https://www.intel.com/content/www/us/en/docs/programmable/683082/21-3/ram-with-byte-enable-signals.html - - // Es una cache one way: cada índice mapea a cada línea de cache - // (directamente mapeada) - - // Define la cantidad de líneas de cache - // Cantidad de bits en addr_index = 12, entonces se le hace left shift 12 - // espacios a 1. Osea, 4096 líneas de cache. - // Tenemos 4kilo-líneas de caché. Cada línea es de 128bits, osea tenemos una - // caché de 64KBi. - localparam DEPTH = 1 << $bits(addr_index); - - // Estas tres secciones constituyen al caché. - // data_file: Donde se guarda la información cacheada. - // tag_file: Se guardan los tags de las líneas de caché. - // state_file: Se guarda el estado de cada línea de caché. (Acá están todos - // los estados de MESI) - 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; - data_file[index_wr] <= data_wr; - end - - if (write_state) - state_file[index_wr] <= state_wr; - - tag_rd <= tag_file[index_rd]; - data_rd <= data_file[index_rd]; - 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) - state_file[i] = INVALID; - -endmodule diff --git a/rtl/cache/token.sv b/rtl/cache/token.sv deleted file mode 100644 index cb3e59d..0000000 --- a/rtl/cache/token.sv +++ /dev/null @@ -1,57 +0,0 @@ -`include "cache/defs.sv" - -module cache_token -#(parameter TOKEN_AT_RESET=0) -( - input logic clk, - rst_n, - - input addr_tag core_tag, - input addr_index core_index, - - input ring_token in_token, // input del token - input logic in_token_valid, // se está recibiendo el token - - output ring_token out_token, // output del token - output logic out_token_valid, // se está enviando el token - - input logic send, - lock_line, - unlock_line, - output logic locked, - may_send -); - - logic may_send_if_token_held; - - // Solo se puede iniciar un request si se tiene el token y el token es - // válido - assign may_send = may_send_if_token_held && in_token_valid; - assign may_send_if_token_held - = (!in_token.e2.valid || in_token.e2.index != core_index || in_token.e2.tag != core_tag) - && (!in_token.e1.valid || in_token.e1.index != core_index || in_token.e1.tag != core_tag) - && (!in_token.e0.valid || in_token.e0.index != core_index || in_token.e0.tag != core_tag); - - always_ff @(posedge clk or negedge rst_n) - if (!rst_n) begin - out_token <= {($bits(out_token)){1'b0}}; - out_token_valid <= TOKEN_AT_RESET; - - locked <= 0; - end else begin - out_token.e0.tag <= core_tag; - out_token.e0.index <= core_index; - out_token.e0.valid <= may_send_if_token_held && (send || locked) && !unlock_line; - - out_token.e2 <= in_token.e1; - out_token.e1 <= in_token.e0; - out_token_valid <= in_token_valid; - - if (lock_line) - locked <= 1; - - if (unlock_line) - locked <= 0; - end - -endmodule -- cgit v1.2.3