URL
https://opencores.org/ocsvn/altor32/altor32/trunk
Subversion Repositories altor32
[/] [altor32/] [trunk/] [rtl/] [cpu/] [altor32_icache.v] - Rev 38
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 - 2014 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 [31:0] wbm_addr_o /*verilator public*/, input [31:0] wbm_dat_i /*verilator public*/, output [2:0] wbm_cti_o /*verilator public*/, output wbm_cyc_o /*verilator public*/, output 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_out_w; reg [CACHE_TAG_WIDTH-1:0] tag_in_r; reg tag_wr_r; // Tag address wire [CACHE_LINE_ADDR_WIDTH-1:0] tag_address_w; // Data memory read / write wire [CACHE_DWIDTH-1:0] address_rd_w; wire [CACHE_DWIDTH-1:0] address_wr_w; wire cache_wr_w; // Current / Miss PC reg [31:0] last_pc_q; reg [31:0] miss_pc_q; // Flush state reg flush_q; reg [CACHE_LINE_ADDR_WIDTH-1:0] flush_addr_q; reg flush_wr_q; // Other state reg read_while_busy_q; // Current state parameter STATE_CHECK = 0; parameter STATE_FETCH = 1; parameter STATE_WAIT = 2; parameter STATE_FLUSH = 3; reg [1:0] state_q; // Tag address from input PC or flopped version of it assign tag_address_w = (state_q != STATE_CHECK) ? miss_pc_q[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 address_rd_w = 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_w = ~tag_out_w[CACHE_TAG_VALID_BIT] | (last_pc_q[CACHE_TAG_ADDR_HIGH:CACHE_TAG_ADDR_LOW] != tag_out_w[14:0]); // Stall the CPU if cache state machine is not idle! wire busy_w = (state_q != STATE_CHECK) | read_while_busy_q; // Cache output valid assign valid_o = busy_w ? 1'b0 : ~miss_w; // Flushing: Last line to flush wire flush_last_w = (flush_addr_q == {CACHE_LINE_ADDR_WIDTH{1'b0}}); // Is this a cache miss? wire cache_miss_w = miss_w & // Tag lookup failed !rd_i & // NOT new read request cycle !read_while_busy_q & // NOT pending read whilst busy !flush_q & // NOT flush request !invalidate_i; wire mem_fetch_w = (state_q == STATE_CHECK) & cache_miss_w; wire [31:0] mem_data_w; wire [31:0] mem_resp_addr_w; wire mem_valid_w; wire mem_final_w; //----------------------------------------------------------------- // Next State Logic //----------------------------------------------------------------- reg [1:0] next_state_r; always @ * begin next_state_r = state_q; case (state_q) //----------------------------------------- // CHECK - check cache for hit or miss //----------------------------------------- STATE_CHECK : begin // Cache flush request pending? if (flush_q || invalidate_i) next_state_r = STATE_FLUSH; // Cache miss (& new read request not pending) else if (cache_miss_w) 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_final_w) next_state_r = STATE_WAIT; end //----------------------------------------- // FLUSH - Invalidate tag memory //----------------------------------------- STATE_FLUSH : begin if (flush_last_w) next_state_r = STATE_CHECK; 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_q <= STATE_FLUSH; else state_q <= next_state_r; end //----------------------------------------------------------------- // Flop request details //----------------------------------------------------------------- always @ (posedge rst_i or posedge clk_i ) begin if (rst_i == 1'b1) begin miss_pc_q <= BOOT_VECTOR + `VECTOR_RESET; last_pc_q <= 32'h00000000; end else begin last_pc_q <= pc_i; case (state_q) //----------------------------------------- // CHECK - check cache for hit or miss //----------------------------------------- STATE_CHECK : begin // Cache hit (or new read request), store fetch PC if (!cache_miss_w) miss_pc_q <= pc_i; end default: ; endcase end end //----------------------------------------------------------------- // Detect new read request whilst busy //----------------------------------------------------------------- always @ (posedge rst_i or posedge clk_i ) begin if (rst_i == 1'b1) begin read_while_busy_q <= 1'b0; end else begin case (state_q) //----------------------------------------- // CHECK - Check cache for hit or miss //----------------------------------------- STATE_CHECK : begin read_while_busy_q <= 1'b0; end //----------------------------------------- // OTHER - Fetching, flushing, waiting //----------------------------------------- default: begin // New request whilst cache busy? if (rd_i) read_while_busy_q <= 1'b1; end endcase end end //----------------------------------------------------------------- // Cache Tag Write //----------------------------------------------------------------- always @ * begin tag_in_r = {CACHE_TAG_WIDTH{1'b0}}; tag_wr_r = 1'b0; case (state_q) //----------------------------------------- // CHECK - check cache for hit or miss //----------------------------------------- STATE_CHECK : begin // Cache miss (& new read request not pending) if (cache_miss_w) begin // Update tag memory with this line's details tag_in_r = {1'b1, miss_pc_q[CACHE_TAG_ADDR_HIGH:CACHE_TAG_ADDR_LOW]}; tag_wr_r = 1'b1; end end default: ; endcase end //----------------------------------------------------------------- // Flush Logic //----------------------------------------------------------------- reg flush_r; reg [CACHE_LINE_ADDR_WIDTH-1:0] flush_addr_r; reg flush_wr_r; always @ * begin flush_wr_r = 1'b0; flush_addr_r = flush_addr_q; flush_r = flush_q; case (state_q) //----------------------------------------- // CHECK - Check cache for hit or miss //----------------------------------------- STATE_CHECK : begin // Cache flush request pending? if (flush_q || invalidate_i) begin flush_r = 1'b0; flush_addr_r = {CACHE_LINE_ADDR_WIDTH{1'b1}}; flush_wr_r = 1'b1; end end //----------------------------------------- // FLUSH - Invalidate tag memory //----------------------------------------- STATE_FLUSH : begin flush_addr_r = flush_addr_q - 1; flush_wr_r = 1'b1; end //----------------------------------------- // OTHER - Fetching / wait cycles //----------------------------------------- default: begin // Latch invalidate request even if can't be actioned now... if (invalidate_i) flush_r = 1'b1; end endcase end always @ (posedge rst_i or posedge clk_i ) begin if (rst_i == 1'b1) begin flush_addr_q <= {CACHE_LINE_ADDR_WIDTH{1'b1}}; flush_wr_q <= 1'b0; flush_q <= 1'b0; end else begin flush_addr_q <= flush_addr_r; flush_wr_q <= flush_wr_r; flush_q <= flush_r; end end //----------------------------------------------------------------- // External Mem Access //----------------------------------------------------------------- altor32_wb_fetch u_wb ( .clk_i(clk_i), .rst_i(rst_i), // Request .fetch_i(mem_fetch_w), .burst_i(1'b1), .address_i(miss_pc_q), // Response .resp_addr_o(mem_resp_addr_w), .data_o(mem_data_w), .valid_o(mem_valid_w), .final_o(mem_final_w), // Wishbone interface .wbm_addr_o(wbm_addr_o), .wbm_dat_i(wbm_dat_i), .wbm_cti_o(wbm_cti_o), .wbm_cyc_o(wbm_cyc_o), .wbm_stb_o(wbm_stb_o), .wbm_stall_i(wbm_stall_i), .wbm_ack_i(wbm_ack_i) ); //----------------------------------------------------------------- // 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_out_w), .adat_i(tag_in_r), .aadr_i(tag_address_w), .awr_i(tag_wr_r), // Tag invalidate port .bclk_i(clk_i), .badr_i(flush_addr_q), .bdat_o(/*open*/), .bdat_i({CACHE_TAG_WIDTH{1'b0}}), .bwr_i(flush_wr_q) ); //----------------------------------------------------------------- // Data memory //----------------------------------------------------------------- altor32_ram_dp #( .WIDTH(32), .SIZE(CACHE_DWIDTH) ) u2_data_mem ( // Data read port .aclk_i(clk_i), .aadr_i(address_rd_w), .adat_o(instruction_o), .adat_i(32'h00), .awr_i(1'b0), // Data write port .bclk_i(clk_i), .badr_i(address_wr_w), .bdat_o(/*open*/), .bdat_i(mem_data_w), .bwr_i(cache_wr_w) ); // Write to cache on wishbone response assign address_wr_w = {miss_pc_q[CACHE_LINE_ADDR_WIDTH + CACHE_LINE_SIZE_WIDTH - 1:CACHE_LINE_SIZE_WIDTH], mem_resp_addr_w[CACHE_LINE_SIZE_WIDTH-1:2]}; assign cache_wr_w = (state_q == STATE_FETCH) & mem_valid_w; endmodule
Go to most recent revision | Compare with Previous | Blame | View Log