`include "defs.svh" module mem_cache #( SET_BITS = 8 ) ( input bit clock , input bit reset , output bit core_command_ready , input bit core_command_valid , input core_to_mem_t core_command_data , input bit ram_command_ready , output bit ram_command_valid , output core_to_mem_t ram_command_data , output bit ram_response_ready , input bit ram_response_valid , input mem_to_core_t ram_response_data , input bit core_response_ready , output bit core_response_valid , output mem_to_core_t core_response_data ); localparam ADDRESS_TAG_LO = $clog2(`RAM_LINE_WORDS)+SET_BITS; typedef bit [`PDP_ADDRESS_BITS-1:ADDRESS_TAG_LO] address_tag_t; typedef bit [ADDRESS_TAG_LO-1:$clog2(`RAM_LINE_WORDS)] set_t; typedef struct packed { bit valid; bit dirty; address_tag_t address; } tag_t; typedef struct packed { tag_t tag; ram_line_t data; } cache_entry_t; struct packed { set_t address; bit read_enable; bit write_enable; cache_entry_t read_data; cache_entry_t write_data; } cache[1:0]; altsyncram #( .address_reg_b("CLOCK0") , .clock_enable_input_a("BYPASS"), .clock_enable_input_b("BYPASS") , .clock_enable_output_a("BYPASS"), .clock_enable_output_b("BYPASS") , .indata_reg_b("CLOCK0") , .numwords_a(1 << SET_BITS), .numwords_b(1 << SET_BITS) , .operation_mode("BIDIR_DUAL_PORT") , .outdata_aclr_a("NONE"), .outdata_aclr_b("NONE") , .outdata_reg_a("UNREGISTERED"), .outdata_reg_b("UNREGISTERED") , .power_up_uninitialized("TRUE") , .ram_block_type("M9K") , .read_during_write_mode_mixed_ports("OLD_DATA") , .read_during_write_mode_port_a("OLD_DATA"), .read_during_write_mode_port_b("OLD_DATA") , .widthad_a(SET_BITS), .widthad_b(SET_BITS) , .width_a($bits(cache_entry_t)), .width_b($bits(cache_entry_t)) , .width_byteena_a(1), .width_byteena_b(1) , .wrcontrol_wraddress_reg_b("CLOCK0") ) cache_controller ( .address_a(cache[0].address), .address_b(cache[1].address) , .clock0(~clock) , .data_a(cache[0].write_data), .data_b(cache[1].write_data) , .rden_a(cache[0].read_enable), .rden_b(cache[1].read_enable) , .wren_a(cache[0].write_enable), .wren_b(cache[1].write_enable) , .q_a(cache[0].read_data), .q_b(cache[1].read_data) , .aclr0(1'b0), .aclr1(1'b0) , .addressstall_a(1'b0), .addressstall_b(1'b0) , .byteena_a(1'b1), .byteena_b(1'b1) , .clock1(1'b1) , .clocken0(1'b1), .clocken1(1'b1), .clocken2(1'b1), .clocken3(1'b1) , .eccstatus() ); bit [SET_BITS:0] reset_entry; // "The" fill buffer address_tag_t working_tag; set_t working_set; (* syn_encoding = "one-hot" *) enum int unsigned { AWAIT_CORE_COMMAND , AWAIT_CACHE , SEND_FILL_REQUEST , AWAIT_RAM_RESPONSE } state; always @(posedge clock) begin if (reset) begin core_command_ready = 0; ram_command_valid = 0; ram_response_ready = 0; core_response_valid = 0; reset_entry = 0; cache[0].address = 0; cache[1].address = 0; cache[0].read_enable = 0; cache[1].read_enable = 0; cache[0].write_enable = 0; cache[1].write_enable = 0; cache[0].write_data = 0; cache[1].write_data = 0; state = state.first; end else begin if (ram_command_ready && ram_command_valid) ram_command_valid = 0; if (core_response_ready && core_response_valid) core_response_valid = 0; if (!reset_entry[SET_BITS]) begin cache[0].address = reset_entry[SET_BITS-1:0]; cache[1].address = reset_entry[SET_BITS-1:0] + 1; cache[0].read_enable = 0; cache[1].read_enable = 0; cache[0].write_enable = 1; cache[1].write_enable = 1; cache[0].write_data = 0; cache[1].write_data = 0; reset_entry += 2; end else begin case (state) AWAIT_CORE_COMMAND: begin cache[0].read_enable = 0; cache[0].write_enable = 0; if (core_command_ready && core_command_valid) begin {working_tag, working_set} = core_command_data.address; cache[0].address = working_set; cache[0].read_enable = 1; cache[0].write_enable = core_command_data.write; cache[0].write_data.tag.valid = 1; cache[0].write_data.tag.dirty = 1; cache[0].write_data.tag.address = working_tag; cache[0].write_data.data = core_command_data.data; state = AWAIT_CACHE; end end AWAIT_CACHE: begin if (cache[0].read_data.tag.valid && cache[0].read_data.tag.dirty && cache[0].read_data.tag.address != working_tag) begin ram_command_valid = 1; ram_command_data.address = {cache[0].read_data.tag.address, working_set}; ram_command_data.write = 1; ram_command_data.snoop_response = 0; ram_command_data.data = cache[0].read_data.data; ram_command_data.mask = ~0; state = cache[0].write_enable ? AWAIT_CORE_COMMAND : SEND_FILL_REQUEST; end else if (cache[0].write_enable) begin core_command_ready = !core_response_valid && !ram_command_valid; state = AWAIT_CORE_COMMAND; end else if (cache[0].read_data.tag.valid && cache[0].read_data.tag.address == working_tag) begin core_response_valid = 1; core_response_data.address = {working_tag, working_set}; core_response_data.snoop = 0; core_response_data.data_valid = 1; core_response_data.data = cache[0].read_data.data; state = AWAIT_CORE_COMMAND; end else begin ram_command_valid = 1; ram_command_data.address = {working_tag, working_set}; ram_command_data.write = 0; ram_command_data.snoop_response = 0; state = AWAIT_RAM_RESPONSE; end cache[0].read_enable = 0; cache[0].write_enable = 0; end SEND_FILL_REQUEST: begin cache[0].read_enable = 0; cache[0].write_enable = 0; if (!ram_command_valid) begin ram_command_valid = 1; ram_command_data.address = {working_tag, working_set}; ram_command_data.write = 0; ram_command_data.snoop_response = 0; state = AWAIT_RAM_RESPONSE; end end AWAIT_RAM_RESPONSE: begin cache[0].read_enable = 0; cache[0].write_enable = 0; if (ram_response_valid && ram_response_data.address == {working_tag, working_set} && ram_response_data.data_valid) begin core_response_valid = 1; core_response_data = ram_response_data; cache[0].address = working_set; cache[0].read_enable = 0; cache[0].write_enable = 1; cache[0].write_data.tag.valid = 1; cache[0].write_data.tag.dirty = 0; cache[0].write_data.tag.address = working_tag; cache[0].write_data.data = ram_response_data.data; state = AWAIT_CORE_COMMAND; end end endcase core_command_ready = state == AWAIT_CORE_COMMAND && !core_response_valid && !ram_command_valid; ram_response_ready = state == AWAIT_RAM_RESPONSE && !core_response_valid && !ram_command_valid; end end end endmodule