diff options
Diffstat (limited to 'rtl/cache/control.sv')
| -rw-r--r-- | rtl/cache/control.sv | 364 |
1 files changed, 364 insertions, 0 deletions
diff --git a/rtl/cache/control.sv b/rtl/cache/control.sv new file mode 100644 index 0000000..f551477 --- /dev/null +++ b/rtl/cache/control.sv @@ -0,0 +1,364 @@ +`include "cache/defs.sv" + +module cache_control +#(parameter TOKEN_AT_RESET=0) +( + input logic clk, + rst_n, + + input addr_tag core_tag, + input addr_index core_index, + input logic core_read, + core_write, + input line core_data_wr, + output logic core_waitrequest, + + input ring_req in_data, + input logic in_data_valid, + output logic in_data_ready, + + input logic out_data_ready, + output ring_req out_data, + output logic out_data_valid, + + input ring_token in_token, + input logic in_token_valid, + + output ring_token out_token, + output logic out_token_valid, + + input addr_tag tag_rd, + input line data_rd, + input line_state state_rd, + + output addr_index index_rd, + index_wr, + output logic write_data, + write_state, + output addr_tag tag_wr, + output line data_wr, + output line_state state_wr, + + input logic mem_waitrequest, + input line mem_readdata, + output word mem_address, + output logic mem_read, + mem_write, + output line mem_writedata +); + + enum int unsigned + { + ACCEPT, + CORE, + SNOOP, + REPLY + } state, next_state; + + logic accept_snoop, in_hold_valid, last_hop, lock_line, locked, may_send, + may_send_if_token_held, mem_begin, mem_end, mem_end_read, mem_wait, + out_stall, reply_wait, replace, retry, send, send_inval, send_read, + snoop_hit, set_reply, unlock_line, writeback; + + ring_req in_hold, send_data, fwd_data, stall_data, out_data_next; + + addr_tag mem_tag; + 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 = {3'b000, mem_tag, mem_index, 4'b0000}; + assign mem_end_read = mem_read && !mem_waitrequest; + + /* Desbloquear la línea hasta que la request del core termine garantiza + * avance del sistema completo, en lockstep en el peor caso posible, + * a pesar de retries (una fuerte contención de writes a INVALID + * o SHARED jamás provocará que dos o más nodos queden en deadlock). + */ + assign unlock_line = !core_waitrequest; + + 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 accept_snoop = in_hold_valid && !last_hop && (in_hold.inval || !in_hold.reply); + + 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); + + 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); + + assign send_data.tag = core_tag; + assign send_data.ttl = `TTL_MAX; + 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 + tag_wr = core_tag; + data_wr = core_data_wr; + index_rd = core_index; + + state_wr = INVALID; + write_data = 0; + write_state = 0; + + mem_begin = 0; + writeback = 0; + + send = 0; + send_read = 0; + send_inval = 0; + + set_reply = 0; + core_waitrequest = 1; + + in_data_ready = !in_hold_valid; + + unique case (state) + ACCEPT: begin + if (last_hop && !in_hold.read) + in_data_ready = 1; + + if (accept_snoop) + index_rd = in_hold.index; + end + + CORE: + if (replace) begin + state_wr = INVALID; + write_state = 1; + + if (state_rd == MODIFIED) + writeback = 1; + end else unique case ({state_rd, core_write}) + {INVALID, 1'b0}: begin + send = 1; + send_read = 1; + end + + {INVALID, 1'b1}: begin + send = 1; + send_read = 1; + send_inval = 1; + end + + {SHARED, 1'b0}: + core_waitrequest = 0; + + {SHARED, 1'b1}: begin + /* No hacemos write_data ya que reintentaremos el + * write luego, cuando el estado será EXCLUSIVE. + * + * Nótese que esta es la misma razón por la que no + * pasamos directamente a MODIFIED. + */ + state_wr = EXCLUSIVE; + write_state = 1; + + send = 1; + send_inval = 1; + end + + {EXCLUSIVE, 1'b0}: + core_waitrequest = 0; + + {EXCLUSIVE, 1'b1}: begin + state_wr = MODIFIED; + write_data = 1; + write_state = 1; + core_waitrequest = 0; + end + + {MODIFIED, 1'b0}: + core_waitrequest = 0; + + {MODIFIED, 1'b1}: begin + write_data = 1; + core_waitrequest = 0; + end + endcase + + SNOOP: begin + index_rd = in_hold.index; + in_data_ready = 1; + + if (snoop_hit) begin + if (in_hold.read) begin + set_reply = 1; + + unique case (state_rd) + INVALID: + set_reply = 0; + + SHARED: ; + + EXCLUSIVE: begin + state_wr = SHARED; + write_state = 1; + end + + MODIFIED: begin + state_wr = SHARED; + write_state = 1; + + writeback = 1; + end + endcase + end + + if (in_hold.inval) begin + state_wr = INVALID; + write_state = 1; + end + end + end + + REPLY: begin + in_data_ready = 1; + + if (in_hold.reply) begin + data_wr = in_hold.data; + state_wr = SHARED; + write_data = 1; + write_state = 1; + end else + mem_begin = 1; + end + + default: ; + endcase + + if (writeback) + mem_begin = 1; + + // Colisiones de bus + retry = (mem_end_read && (write_data || write_state)) || (mem_wait && mem_begin); + + // Nótese la diferencia con un assign, ya que send puede cambiar más abajo + lock_line = send; + + if (send && !may_send && !locked) + retry = 1; + + if (retry) begin + send = 0; + mem_begin = 0; + write_data = 0; + write_state = 0; + + in_data_ready = !in_hold_valid; + core_waitrequest = 1; + end + + index_wr = index_rd; + if (mem_end_read) begin + tag_wr = mem_tag; + index_wr = mem_index; + + data_wr = mem_readdata; + state_wr = EXCLUSIVE; + + write_data = 1; + write_state = 1; + end + end + + 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_comb begin + next_state = ACCEPT; + + unique case (state) + ACCEPT: begin + if (accept_snoop) + next_state = SNOOP; + else if (in_hold_valid && last_hop && in_hold.read) + next_state = REPLY; + else if ((core_read || core_write) && !reply_wait + && (!locked || (may_send && !unlock_line))) + next_state = CORE; + + if (out_stall && !out_data_ready) + next_state = ACCEPT; + end + + default: ; + endcase + end + + always_ff @(posedge clk or negedge rst_n) + state <= !rst_n ? ACCEPT : next_state; + + 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; + + in_hold_valid <= 0; + out_stall <= 0; + + locked <= 0; + + mem_read <= 0; + mem_write <= 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 (in_data_ready) + in_hold_valid <= in_data_valid; + + out_stall <= out_data_valid && !out_data_ready; + + if (lock_line) + locked <= 1; + + if (unlock_line) + locked <= 0; + + 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) begin + if (in_data_ready) + in_hold <= in_data; + + if (!out_stall) + stall_data <= out_data_next; + + if (mem_begin) begin + mem_tag <= writeback ? tag_rd : core_tag; + mem_index <= index_wr; + mem_writedata <= data_rd; + end + end + +endmodule |
