URL
https://opencores.org/ocsvn/altor32/altor32/trunk
Subversion Repositories altor32
[/] [altor32/] [trunk/] [rtl/] [cpu/] [altor32_icache.v] - Rev 36
Go to most recent revision | Compare with Previous | Blame | View Log
//----------------------------------------------------------------- // AltOR32 // Alternative Lightweight OpenRisc // V2.1 // Ultra-Embedded.com // Copyright 2011 - 2014 // // Email: admin@ultra-embedded.com // // License: LGPL //----------------------------------------------------------------- // // Copyright (C) 2011 - 2013 Ultra-Embedded.com // // This source file may be used and distributed without // restriction provided that this copyright statement is not // removed from the file and that any derivative work contains // the original copyright notice and the associated disclaimer. // // This source file is free software; you can redistribute it // and/or modify it under the terms of the GNU Lesser General // Public License as published by the Free Software Foundation; // either version 2.1 of the License, or (at your option) any // later version. // // This source is distributed in the hope that it will be // useful, but WITHOUT ANY WARRANTY; without even the implied // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the GNU Lesser General Public License for more // details. // // You should have received a copy of the GNU Lesser General // Public License along with this source; if not, write to the // Free Software Foundation, Inc., 59 Temple Place, Suite 330, // Boston, MA 02111-1307 USA //----------------------------------------------------------------- //----------------------------------------------------------------- // Includes //----------------------------------------------------------------- `include "altor32_defs.v" //----------------------------------------------------------------- // Module - Instruction Cache //----------------------------------------------------------------- module altor32_icache ( input clk_i /*verilator public*/, input rst_i /*verilator public*/, // Processor interface input rd_i /*verilator public*/, input [31:0] pc_i /*verilator public*/, output [31:0] instruction_o /*verilator public*/, output valid_o /*verilator public*/, input invalidate_i /*verilator public*/, // Memory interface output reg [31:0] wbm_addr_o /*verilator public*/, input [31:0] wbm_dat_i /*verilator public*/, output reg [2:0] wbm_cti_o /*verilator public*/, output reg wbm_cyc_o /*verilator public*/, output reg wbm_stb_o /*verilator public*/, input wbm_stall_i/*verilator public*/, input wbm_ack_i/*verilator public*/ ); //----------------------------------------------------------------- // Params //----------------------------------------------------------------- parameter BOOT_VECTOR = 32'h00000000; parameter CACHE_LINE_SIZE_WIDTH = 5; /* 5-bits -> 32 entries */ parameter CACHE_LINE_SIZE_BYTES = 2 ** CACHE_LINE_SIZE_WIDTH; /* 32 bytes / 4 words per line */ parameter CACHE_LINE_ADDR_WIDTH = 8; /* 256 lines */ parameter CACHE_LINE_WORDS_IDX_MAX = CACHE_LINE_SIZE_WIDTH - 2; /* 3-bit = 111 */ parameter CACHE_TAG_ENTRIES = 2 ** CACHE_LINE_ADDR_WIDTH ; /* 256 tag entries */ parameter CACHE_DSIZE = CACHE_LINE_ADDR_WIDTH * CACHE_LINE_SIZE_BYTES; /* 8KB data */ parameter CACHE_DWIDTH = CACHE_LINE_ADDR_WIDTH + CACHE_LINE_SIZE_WIDTH - 2; /* 10-bits */ parameter CACHE_TAG_WIDTH = 16; /* 16-bit tag entry size */ parameter CACHE_TAG_LINE_ADDR_WIDTH = CACHE_TAG_WIDTH - 1; /* 15 bits of data (tag entry size minus valid bit) */ parameter CACHE_TAG_ADDR_LOW = CACHE_LINE_SIZE_WIDTH + CACHE_LINE_ADDR_WIDTH; parameter CACHE_TAG_ADDR_HIGH = CACHE_TAG_LINE_ADDR_WIDTH + CACHE_LINE_SIZE_WIDTH + CACHE_LINE_ADDR_WIDTH - 1; // Tag fields parameter CACHE_TAG_VALID_BIT = 15; // 31 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00 // |--------------| | | | | | | | | | | | | | | | | // +--------------------+ +-------------------+ +-----------+ // Tag entry Line address Address // (15-bits) (8-bits) within line // (5-bits) //----------------------------------------------------------------- // Registers / Wires //----------------------------------------------------------------- // Tag read / write data wire [CACHE_TAG_WIDTH-1:0] tag_data_out; reg [CACHE_TAG_WIDTH-1:0] tag_data_in; reg tag_wr; // Tag address wire [CACHE_LINE_ADDR_WIDTH-1:0] tag_entry; // Data memory read / write wire [CACHE_DWIDTH-1:0] cache_address_rd; wire [CACHE_DWIDTH-1:0] cache_address_wr; reg [31:0] cache_data_in; wire cache_wr; // Word currently being fetched within a line reg [CACHE_LINE_SIZE_WIDTH-3:0] mem_fetch_word; reg [CACHE_LINE_SIZE_WIDTH-3:0] mem_resp_idx; // Current / Miss PC reg [31:0] last_pc; reg [31:0] miss_pc; // Flush state reg flush_req; reg [CACHE_LINE_ADDR_WIDTH-1:0] flush_addr; reg flush_wr; // Other state reg initial_fetch; reg read_while_busy; // Current state parameter STATE_CHECK = 0; parameter STATE_FETCH = 1; parameter STATE_WAIT = 2; parameter STATE_FLUSH = 3; reg [3:0] state; // Tag address from input PC or flopped version of it assign tag_entry = (state != STATE_CHECK) ? miss_pc[CACHE_LINE_ADDR_WIDTH + CACHE_LINE_SIZE_WIDTH - 1:CACHE_LINE_SIZE_WIDTH] : pc_i[CACHE_LINE_ADDR_WIDTH + CACHE_LINE_SIZE_WIDTH - 1:CACHE_LINE_SIZE_WIDTH]; // Cache read address assign cache_address_rd = pc_i[CACHE_LINE_ADDR_WIDTH + CACHE_LINE_SIZE_WIDTH - 1:2]; // Cache miss output if requested PC is not in the tag memory wire miss = (!tag_data_out[CACHE_TAG_VALID_BIT] || (last_pc[CACHE_TAG_ADDR_HIGH:CACHE_TAG_ADDR_LOW] != tag_data_out[14:0])) ? 1'b1: 1'b0; // Stall the CPU if cache state machine is not idle! wire busy = (state == STATE_CHECK & ~read_while_busy) ? 1'b0 : 1'b1; // Cache output valid assign valid_o = !miss && !busy; // Final word to fetch from memory wire mem_fetch_final_word = (mem_fetch_word == {CACHE_LINE_WORDS_IDX_MAX{1'b1}}); // Flushing: Last line to flush wire flush_final_line = (flush_addr == {CACHE_LINE_ADDR_WIDTH{1'b0}}); // Is this a cache miss? wire cache_miss = (miss && // Tag lookup failed !initial_fetch && // NOT initial fetch after reset !rd_i && // NOT new read request cycle !read_while_busy && // NOT pending read whilst busy !flush_req && // NOT flush request !invalidate_i); //----------------------------------------------------------------- // Next State Logic //----------------------------------------------------------------- reg [3:0] next_state_r; always @ * begin next_state_r = state; case (state) //----------------------------------------- // CHECK - check cache for hit or miss //----------------------------------------- STATE_CHECK : begin // Cache flush request pending? if (flush_req || invalidate_i) next_state_r = STATE_FLUSH; // Cache miss (& new read request not pending) else if (cache_miss) next_state_r = STATE_FETCH; // Cache hit (or new read request) else next_state_r = STATE_CHECK; end //----------------------------------------- // FETCH - Fetch row from memory //----------------------------------------- STATE_FETCH : begin // Line fetch complete? if (mem_resp_idx == {CACHE_LINE_SIZE_WIDTH-2{1'b1}} && wbm_ack_i) next_state_r = STATE_WAIT; end //----------------------------------------- // FLUSH - Invalidate tag memory //----------------------------------------- STATE_FLUSH : begin if (flush_final_line) next_state_r = STATE_FETCH; else next_state_r = STATE_FLUSH; end //----------------------------------------- // WAIT - Wait cycle //----------------------------------------- STATE_WAIT : next_state_r = STATE_CHECK; default: ; endcase end // Update state always @ (posedge rst_i or posedge clk_i ) begin if (rst_i == 1'b1) state <= STATE_CHECK; else state <= next_state_r; end //----------------------------------------------------------------- // Check for cache misses //----------------------------------------------------------------- always @ (posedge rst_i or posedge clk_i ) begin if (rst_i == 1'b1) begin miss_pc <= BOOT_VECTOR + `VECTOR_RESET; last_pc <= 32'h00000000; initial_fetch <= 1'b1; read_while_busy <= 1'b0; end else begin initial_fetch <= 1'b0; last_pc <= pc_i; // New request whilst cache busy? if (rd_i) read_while_busy <= 1'b1; case (state) //----------------------------------------- // CHECK - check cache for hit or miss //----------------------------------------- STATE_CHECK : begin // Cache miss (& new read request not pending) if (cache_miss) begin read_while_busy <= 1'b0; `ifdef CONF_CORE_DEBUG $display("Fetch: Cache miss at 0x%x (last=%x, current=%x)", miss_pc, last_pc, pc_i); `endif end // Cache hit (or new read request) else begin `ifdef CONF_CORE_DEBUG $display("Fetch: Cache hit at PC=%x (current=%x)", last_pc, pc_i); if (read_while_busy) $display("Fetch: Read request whilst busy PC=%x (current=%x)", last_pc, pc_i); `endif // Store fetch PC miss_pc <= pc_i; read_while_busy <= 1'b0; end end //----------------------------------------- // FLUSH - Invalidate tag memory //----------------------------------------- STATE_FLUSH : begin // Last line, clear pending reads whilst busy if (flush_final_line) read_while_busy <= 1'b0; end default: ; endcase end end //----------------------------------------------------------------- // Cache Tag Write //----------------------------------------------------------------- always @ (posedge rst_i or posedge clk_i ) begin if (rst_i == 1'b1) begin tag_data_in <= {CACHE_TAG_WIDTH{1'b0}}; tag_wr <= 1'b0; end else begin tag_wr <= 1'b0; case (state) //----------------------------------------- // CHECK - check cache for hit or miss //----------------------------------------- STATE_CHECK : begin // Cache miss (& new read request not pending) if (cache_miss) begin // Update tag memory with this line's details tag_data_in <= {1'b1, miss_pc[CACHE_TAG_ADDR_HIGH:CACHE_TAG_ADDR_LOW]}; tag_wr <= 1'b1; end end //----------------------------------------- // FLUSH - Invalidate tag memory //----------------------------------------- STATE_FLUSH : begin if (flush_final_line) begin // Update tag memory with this line's details tag_data_in <= {1'b1, pc_i[CACHE_TAG_ADDR_HIGH:CACHE_TAG_ADDR_LOW]}; tag_wr <= 1'b1; end end default: ; endcase end end //----------------------------------------------------------------- // Flush Logic //----------------------------------------------------------------- always @ (posedge rst_i or posedge clk_i ) begin if (rst_i == 1'b1) begin flush_addr <= {CACHE_LINE_ADDR_WIDTH{1'b0}}; flush_wr <= 1'b0; flush_req <= 1'b0; end else begin flush_wr <= 1'b0; // Latch invalidate request even if can't be actioned now... if (invalidate_i) flush_req <= 1'b1; case (state) //----------------------------------------- // CHECK - check cache for hit or miss //----------------------------------------- STATE_CHECK : begin // Cache flush request pending? if (flush_req || invalidate_i) begin flush_req <= 1'b0; flush_addr <= {CACHE_LINE_ADDR_WIDTH{1'b1}}; flush_wr <= 1'b1; `ifdef CONF_CORE_DEBUG $display("Fetch: Cache flush request"); `endif end end //----------------------------------------- // FLUSH - Invalidate tag memory //----------------------------------------- STATE_FLUSH : begin if (~flush_final_line) begin flush_addr <= flush_addr - 1; flush_wr <= 1'b1; end end default: ; endcase end end //----------------------------------------------------------------- // External Mem Access //----------------------------------------------------------------- // Next fetch address wire [CACHE_LINE_SIZE_WIDTH-3:0] mem_next_word = mem_fetch_word + 1; // Last word to fetch wire mem_resp_idx_word = (mem_fetch_word == ({CACHE_LINE_WORDS_IDX_MAX{1'b1}}-1)); always @ (posedge rst_i or posedge clk_i ) begin if (rst_i == 1'b1) begin wbm_addr_o <= 32'h00000000; wbm_cti_o <= 3'b0; wbm_stb_o <= 1'b0; mem_fetch_word <= {CACHE_LINE_SIZE_WIDTH-2{1'b0}}; end else begin if (~wbm_stall_i) wbm_stb_o <= 1'b0; case (state) //----------------------------------------- // CHECK - check cache for hit or miss //----------------------------------------- STATE_CHECK : begin // Cache miss (& new read request not pending) if (cache_miss) begin // Start fetch from memory wbm_addr_o <= {miss_pc[31:CACHE_LINE_SIZE_WIDTH], {CACHE_LINE_SIZE_WIDTH{1'b0}}}; // Incrementing linear burst wbm_cti_o <= 3'b010; // Start of cycle wbm_stb_o <= 1'b1; mem_fetch_word <= {CACHE_LINE_SIZE_WIDTH-2{1'b0}}; end end //----------------------------------------- // FETCH - Fetch row from memory //----------------------------------------- STATE_FETCH : begin // Command accepted if (~wbm_stall_i) begin // Fetch next word for line if (!mem_fetch_final_word) begin wbm_addr_o <= {wbm_addr_o[31:CACHE_LINE_SIZE_WIDTH], mem_next_word, 2'b00}; // Final word to read? if (mem_resp_idx_word) wbm_cti_o <= 3'b111; mem_fetch_word <= mem_next_word; // Still fetching... wbm_stb_o <= 1'b1; end end end //----------------------------------------- // FLUSH - Invalidate tag memory //----------------------------------------- STATE_FLUSH : begin // Fetch current PC line again if (flush_final_line) begin if (read_while_busy) wbm_addr_o <= {miss_pc[31:CACHE_LINE_SIZE_WIDTH], {CACHE_LINE_SIZE_WIDTH{1'b0}}}; else wbm_addr_o <= {pc_i[31:CACHE_LINE_SIZE_WIDTH], {CACHE_LINE_SIZE_WIDTH{1'b0}}}; // Incrementing linear burst wbm_cti_o <= 3'b010; // Start of cycle wbm_stb_o <= 1'b1; // Start of line mem_fetch_word <= {CACHE_LINE_SIZE_WIDTH-2{1'b0}}; end end default: ; endcase end end //----------------------------------------------------------------- // CYC_O //----------------------------------------------------------------- always @ (posedge rst_i or posedge clk_i ) begin if (rst_i == 1'b1) wbm_cyc_o <= 1'b0; else begin case (state) //----------------------------------------- // CHECK - check cache for hit or miss //----------------------------------------- STATE_CHECK : begin // Cache miss (& new read request not pending) if (cache_miss) wbm_cyc_o <= 1'b1; end //----------------------------------------- // FLUSH - Invalidate tag memory //----------------------------------------- STATE_FLUSH : begin // Fetch current PC line again if (flush_final_line) wbm_cyc_o <= 1'b1; end //----------------------------------------- // FETCH - Fetch row from memory //----------------------------------------- STATE_FETCH : begin // Last response? if (wbm_ack_i && (mem_resp_idx == {CACHE_LINE_SIZE_WIDTH-2{1'b1}})) wbm_cyc_o <= 1'b0; end default: ; endcase end end //----------------------------------------------------------------- // Memory response counter //----------------------------------------------------------------- always @ (posedge rst_i or posedge clk_i ) begin if (rst_i == 1'b1) mem_resp_idx <= {CACHE_LINE_SIZE_WIDTH-2{1'b0}}; else begin case (state) //----------------------------------------- // CHECK - check cache for hit or miss //----------------------------------------- STATE_CHECK : begin // Cache miss (& new read request not pending) if (cache_miss) mem_resp_idx <= {CACHE_LINE_SIZE_WIDTH-2{1'b0}}; end //----------------------------------------- // FLUSH - Invalidate tag memory //----------------------------------------- STATE_FLUSH : begin // Fetch current PC line again if (flush_final_line) mem_resp_idx <= {CACHE_LINE_SIZE_WIDTH-2{1'b0}}; end //----------------------------------------- // FETCH - Fetch row from memory //----------------------------------------- STATE_FETCH : begin // Response if (wbm_ack_i) mem_resp_idx <= mem_resp_idx + 1; end default: ; endcase end end //----------------------------------------------------------------- // Tag memory //----------------------------------------------------------------- altor32_ram_dp #( .WIDTH(CACHE_TAG_WIDTH), .SIZE(CACHE_LINE_ADDR_WIDTH) ) u1_tag_mem ( // Tag read/write port .aclk_i(clk_i), .adat_o(tag_data_out), .adat_i(tag_data_in), .aadr_i(tag_entry), .awr_i(tag_wr), // Tag invalidate port .bclk_i(clk_i), .badr_i(flush_addr), .bdat_o(/*open*/), .bdat_i({CACHE_TAG_WIDTH{1'b0}}), .bwr_i(flush_wr) ); //----------------------------------------------------------------- // Data memory //----------------------------------------------------------------- altor32_ram_dp #( .WIDTH(32), .SIZE(CACHE_DWIDTH) ) u2_data_mem ( // Data read port .aclk_i(clk_i), .aadr_i(cache_address_rd), .adat_o(instruction_o), .adat_i(32'h00), .awr_i(1'b0), // Data write port .bclk_i(clk_i), .badr_i(cache_address_wr), .bdat_o(/*open*/), .bdat_i(wbm_dat_i), .bwr_i(cache_wr) ); // Write to cache on wishbone response assign cache_address_wr = {miss_pc[CACHE_LINE_ADDR_WIDTH + CACHE_LINE_SIZE_WIDTH - 1:CACHE_LINE_SIZE_WIDTH], mem_resp_idx}; assign cache_wr = (state == STATE_FETCH) & wbm_ack_i; endmodule
Go to most recent revision | Compare with Previous | Blame | View Log