//-----------------------------------------------------------------
|
//-----------------------------------------------------------------
|
// AltOR32
|
// AltOR32
|
// Alternative Lightweight OpenRisc
|
// Alternative Lightweight OpenRisc
|
// V2.0
|
// V2.0
|
// Ultra-Embedded.com
|
// Ultra-Embedded.com
|
// Copyright 2011 - 2013
|
// Copyright 2011 - 2013
|
//
|
//
|
// Email: admin@ultra-embedded.com
|
// Email: admin@ultra-embedded.com
|
//
|
//
|
// License: LGPL
|
// License: LGPL
|
//-----------------------------------------------------------------
|
//-----------------------------------------------------------------
|
//
|
//
|
// Copyright (C) 2011 - 2013 Ultra-Embedded.com
|
// Copyright (C) 2011 - 2013 Ultra-Embedded.com
|
//
|
//
|
// This source file may be used and distributed without
|
// This source file may be used and distributed without
|
// restriction provided that this copyright statement is not
|
// restriction provided that this copyright statement is not
|
// removed from the file and that any derivative work contains
|
// removed from the file and that any derivative work contains
|
// the original copyright notice and the associated disclaimer.
|
// the original copyright notice and the associated disclaimer.
|
//
|
//
|
// This source file is free software; you can redistribute it
|
// This source file is free software; you can redistribute it
|
// and/or modify it under the terms of the GNU Lesser General
|
// and/or modify it under the terms of the GNU Lesser General
|
// Public License as published by the Free Software Foundation;
|
// Public License as published by the Free Software Foundation;
|
// either version 2.1 of the License, or (at your option) any
|
// either version 2.1 of the License, or (at your option) any
|
// later version.
|
// later version.
|
//
|
//
|
// This source is distributed in the hope that it will be
|
// This source is distributed in the hope that it will be
|
// useful, but WITHOUT ANY WARRANTY; without even the implied
|
// useful, but WITHOUT ANY WARRANTY; without even the implied
|
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
// PURPOSE. See the GNU Lesser General Public License for more
|
// PURPOSE. See the GNU Lesser General Public License for more
|
// details.
|
// details.
|
//
|
//
|
// You should have received a copy of the GNU Lesser General
|
// You should have received a copy of the GNU Lesser General
|
// Public License along with this source; if not, write to the
|
// Public License along with this source; if not, write to the
|
// Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
// Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
// Boston, MA 02111-1307 USA
|
// Boston, MA 02111-1307 USA
|
//-----------------------------------------------------------------
|
//-----------------------------------------------------------------
|
|
|
//-----------------------------------------------------------------
|
//-----------------------------------------------------------------
|
// Module - Data Cache (write back)
|
// Module - Data Cache (write back)
|
//-----------------------------------------------------------------
|
//-----------------------------------------------------------------
|
module altor32_dcache
|
module altor32_dcache
|
(
|
(
|
input clk_i /*verilator public*/,
|
input clk_i /*verilator public*/,
|
input rst_i /*verilator public*/,
|
input rst_i /*verilator public*/,
|
|
|
input flush_i /*verilator public*/,
|
input flush_i /*verilator public*/,
|
|
|
// Input (CPU)
|
// Input (CPU)
|
input [31:0] address_i /*verilator public*/,
|
input [31:0] address_i /*verilator public*/,
|
output [31:0] data_o /*verilator public*/,
|
output [31:0] data_o /*verilator public*/,
|
input [31:0] data_i /*verilator public*/,
|
input [31:0] data_i /*verilator public*/,
|
input rd_i /*verilator public*/,
|
input we_i /*verilator public*/,
|
input [3:0] wr_i /*verilator public*/,
|
input stb_i /*verilator public*/,
|
output accept_o /*verilator public*/,
|
input [3:0] sel_i /*verilator public*/,
|
|
output stall_o /*verilator public*/,
|
output ack_o /*verilator public*/,
|
output ack_o /*verilator public*/,
|
|
|
// Output (Memory)
|
// Output (Memory)
|
output [31:0] mem_addr_o /*verilator public*/,
|
output [31:0] mem_addr_o /*verilator public*/,
|
input [31:0] mem_data_i /*verilator public*/,
|
input [31:0] mem_data_i /*verilator public*/,
|
output [31:0] mem_data_o /*verilator public*/,
|
output [31:0] mem_data_o /*verilator public*/,
|
output mem_burst_o /*verilator public*/,
|
output [2:0] mem_cti_o /*verilator public*/,
|
output mem_rd_o /*verilator public*/,
|
output mem_cyc_o /*verilator public*/,
|
output [3:0] mem_wr_o /*verilator public*/,
|
output mem_stb_o /*verilator public*/,
|
input mem_accept_i/*verilator public*/,
|
output mem_we_o /*verilator public*/,
|
|
output [3:0] mem_sel_o /*verilator public*/,
|
|
input mem_stall_i/*verilator public*/,
|
input mem_ack_i/*verilator public*/
|
input mem_ack_i/*verilator public*/
|
);
|
);
|
|
|
//-----------------------------------------------------------------
|
//-----------------------------------------------------------------
|
// Params
|
// Params
|
//-----------------------------------------------------------------
|
//-----------------------------------------------------------------
|
parameter CACHE_LINE_SIZE_WIDTH = 5; /* 5-bits -> 32 entries */
|
parameter CACHE_LINE_SIZE_WIDTH = 5; /* 5-bits -> 32 entries */
|
parameter CACHE_LINE_SIZE_BYTES = 2 ** CACHE_LINE_SIZE_WIDTH; /* 32 bytes / 8 words per line */
|
parameter CACHE_LINE_SIZE_BYTES = 2 ** CACHE_LINE_SIZE_WIDTH; /* 32 bytes / 8 words per line */
|
parameter CACHE_LINE_ADDR_WIDTH = 8; /* 256 lines */
|
parameter CACHE_LINE_ADDR_WIDTH = 8; /* 256 lines */
|
parameter CACHE_LINE_WORDS_IDX_MAX = CACHE_LINE_SIZE_WIDTH - 2; /* 3-bit = 8 words */
|
parameter CACHE_LINE_WORDS_IDX_MAX = CACHE_LINE_SIZE_WIDTH - 2; /* 3-bit = 8 words */
|
parameter CACHE_TAG_ENTRIES = 2 ** CACHE_LINE_ADDR_WIDTH ; /* 256 tag entries */
|
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_DSIZE = CACHE_LINE_ADDR_WIDTH * CACHE_LINE_SIZE_BYTES; /* 8KB data */
|
parameter CACHE_DWIDTH = CACHE_LINE_ADDR_WIDTH + CACHE_LINE_SIZE_WIDTH - 2; /* 11-bits */
|
parameter CACHE_DWIDTH = CACHE_LINE_ADDR_WIDTH + CACHE_LINE_SIZE_WIDTH - 2; /* 11-bits */
|
|
|
parameter CACHE_TAG_WIDTH = 16; /* 16-bit tag entry size */
|
parameter CACHE_TAG_WIDTH = 16; /* 16-bit tag entry size */
|
parameter CACHE_TAG_LINE_ADDR_WIDTH = CACHE_TAG_WIDTH - 2; /* 14 bits of data (tag entry size minus valid/dirty bit) */
|
parameter CACHE_TAG_LINE_ADDR_WIDTH = CACHE_TAG_WIDTH - 2; /* 14 bits of data (tag entry size minus valid/dirty bit) */
|
|
|
parameter CACHE_TAG_ADDR_LOW = CACHE_LINE_SIZE_WIDTH + CACHE_LINE_ADDR_WIDTH;
|
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;
|
parameter CACHE_TAG_ADDR_HIGH = CACHE_TAG_LINE_ADDR_WIDTH + CACHE_LINE_SIZE_WIDTH + CACHE_LINE_ADDR_WIDTH - 1;
|
|
|
// Tag fields
|
// Tag fields
|
parameter CACHE_TAG_DIRTY_BIT = 14;
|
parameter CACHE_TAG_DIRTY_BIT = 14;
|
parameter CACHE_TAG_VALID_BIT = 15;
|
parameter CACHE_TAG_VALID_BIT = 15;
|
parameter ADDR_NO_CACHE_BIT = 25;
|
parameter ADDR_NO_CACHE_BIT = 25;
|
parameter ADDR_CACHE_BYPASS_BIT = 31;
|
parameter ADDR_CACHE_BYPASS_BIT = 31;
|
|
|
// 31 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
|
// 31 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
|
// |--------------| | | | | | | | | | | | | | | | |
|
// |--------------| | | | | | | | | | | | | | | | |
|
// +--------------------+ +-------------------+ +-----------+
|
// +--------------------+ +-------------------+ +-----------+
|
// Tag entry Line address Address
|
// Tag entry Line address Address
|
// (15-bits) (8-bits) within line
|
// (15-bits) (8-bits) within line
|
// (5-bits)
|
// (5-bits)
|
|
|
//-----------------------------------------------------------------
|
//-----------------------------------------------------------------
|
// Registers / Wires
|
// Registers / Wires
|
//-----------------------------------------------------------------
|
//-----------------------------------------------------------------
|
wire [CACHE_LINE_ADDR_WIDTH-1:0] tag_entry;
|
wire [CACHE_LINE_ADDR_WIDTH-1:0] tag_entry;
|
wire [CACHE_TAG_WIDTH-1:0] tag_data_out;
|
wire [CACHE_TAG_WIDTH-1:0] tag_data_out;
|
reg [CACHE_TAG_WIDTH-1:0] tag_data_in;
|
reg [CACHE_TAG_WIDTH-1:0] tag_data_in;
|
reg tag_wr;
|
reg tag_wr;
|
|
|
wire [CACHE_DWIDTH-1:0] cache_address;
|
wire [CACHE_DWIDTH-1:0] cache_address;
|
wire [31:0] cache_data_r;
|
wire [31:0] cache_data_r;
|
reg [31:0] cache_data_w;
|
reg [31:0] cache_data_w;
|
reg [3:0] cache_wr;
|
reg [3:0] cache_wr;
|
|
|
wire [31:2] cache_update_addr;
|
wire [31:2] cache_update_addr;
|
wire [31:0] cache_update_data_w;
|
wire [31:0] cache_update_data_w;
|
wire [31:0] cache_update_data_r;
|
wire [31:0] cache_update_data_r;
|
wire cache_update_wr;
|
wire cache_update_wr;
|
|
|
reg ack;
|
reg ack;
|
|
|
reg fill;
|
reg fill;
|
reg evict;
|
reg evict;
|
wire done;
|
wire done;
|
|
|
reg [31:0] data_w;
|
|
wire [31:0] data_r;
|
wire [31:0] data_r;
|
reg data_rd;
|
reg rd_single;
|
reg [3:0] data_wr;
|
reg [3:0] wr_single;
|
|
|
reg req_rd;
|
reg req_rd;
|
reg [3:0] req_wr;
|
reg [3:0] req_wr;
|
reg req_ack;
|
reg req_ack;
|
reg [31:0] req_address;
|
reg [31:0] req_address;
|
|
reg [31:0] req_data;
|
|
|
reg req_flush;
|
reg req_flush;
|
reg flush_single;
|
reg flush_single;
|
|
|
wire [31:0] line_address;
|
wire [31:0] line_address;
|
|
|
wire [31:0] muxed_address = (state == STATE_IDLE) ? address_i : req_address;
|
wire [31:0] muxed_address = (state == STATE_IDLE) ? address_i : req_address;
|
|
|
// Current state
|
// Current state
|
parameter STATE_IDLE = 0;
|
parameter STATE_IDLE = 0;
|
parameter STATE_SINGLE = 1;
|
parameter STATE_SINGLE = 1;
|
parameter STATE_CHECK = 2;
|
parameter STATE_CHECK = 2;
|
parameter STATE_FETCH = 3;
|
parameter STATE_FETCH = 3;
|
parameter STATE_WAIT = 4;
|
parameter STATE_WAIT = 4;
|
parameter STATE_WAIT2 = 5;
|
parameter STATE_WAIT2 = 5;
|
parameter STATE_WRITE = 6;
|
parameter STATE_WRITE = 6;
|
parameter STATE_SINGLE_READY= 7;
|
parameter STATE_SINGLE_READY= 7;
|
parameter STATE_EVICTING = 8;
|
parameter STATE_EVICTING = 8;
|
parameter STATE_UPDATE = 9;
|
parameter STATE_UPDATE = 9;
|
parameter STATE_FLUSH1 = 10;
|
parameter STATE_FLUSH1 = 10;
|
parameter STATE_FLUSH2 = 11;
|
parameter STATE_FLUSH2 = 11;
|
parameter STATE_FLUSH3 = 12;
|
parameter STATE_FLUSH3 = 12;
|
parameter STATE_FLUSH4 = 13;
|
parameter STATE_FLUSH4 = 13;
|
reg [3:0] state;
|
reg [3:0] state;
|
|
|
assign tag_entry = muxed_address[CACHE_LINE_ADDR_WIDTH + CACHE_LINE_SIZE_WIDTH - 1:CACHE_LINE_SIZE_WIDTH];
|
assign tag_entry = muxed_address[CACHE_LINE_ADDR_WIDTH + CACHE_LINE_SIZE_WIDTH - 1:CACHE_LINE_SIZE_WIDTH];
|
assign cache_address = {tag_entry, muxed_address[CACHE_LINE_SIZE_WIDTH-1:2]};
|
assign cache_address = {tag_entry, muxed_address[CACHE_LINE_SIZE_WIDTH-1:2]};
|
|
|
assign data_o = (state == STATE_SINGLE_READY) ? data_r : cache_data_r;
|
assign data_o = (state == STATE_SINGLE_READY) ? data_r : cache_data_r;
|
assign accept_o = (state == STATE_IDLE);
|
assign stall_o = (state != STATE_IDLE) | req_flush;
|
|
|
|
|
wire valid = tag_data_out[CACHE_TAG_VALID_BIT];
|
wire valid = tag_data_out[CACHE_TAG_VALID_BIT];
|
wire dirty = tag_data_out[CACHE_TAG_DIRTY_BIT];
|
wire dirty = tag_data_out[CACHE_TAG_DIRTY_BIT];
|
|
|
// Access is cacheable?
|
// Access is cacheable?
|
wire cacheable = ~muxed_address[ADDR_NO_CACHE_BIT] & ~muxed_address[ADDR_CACHE_BYPASS_BIT];
|
wire cacheable = ~muxed_address[ADDR_NO_CACHE_BIT] & ~muxed_address[ADDR_CACHE_BYPASS_BIT];
|
|
|
|
// Address matches cache tag
|
|
wire addr_hit = (req_address[CACHE_TAG_ADDR_HIGH:CACHE_TAG_ADDR_LOW] == tag_data_out[13:0]);
|
|
|
// Cache hit?
|
// Cache hit?
|
wire hit = cacheable & valid & (muxed_address[CACHE_TAG_ADDR_HIGH:CACHE_TAG_ADDR_LOW] == tag_data_out[13:0]) & (state == STATE_CHECK);
|
wire hit = cacheable & valid & addr_hit & (state == STATE_CHECK);
|
|
|
assign ack_o = ack | hit;
|
assign ack_o = ack | hit;
|
|
|
assign line_address[CACHE_LINE_ADDR_WIDTH + CACHE_LINE_SIZE_WIDTH - 1:CACHE_LINE_SIZE_WIDTH] = tag_entry;
|
assign line_address[CACHE_LINE_ADDR_WIDTH + CACHE_LINE_SIZE_WIDTH - 1:CACHE_LINE_SIZE_WIDTH] = tag_entry;
|
assign line_address[CACHE_TAG_ADDR_HIGH:CACHE_TAG_ADDR_LOW] = tag_data_out[13:0];
|
assign line_address[CACHE_TAG_ADDR_HIGH:CACHE_TAG_ADDR_LOW] = tag_data_out[13:0];
|
assign line_address[CACHE_LINE_SIZE_WIDTH-1:0] = {CACHE_LINE_SIZE_WIDTH{1'b0}};
|
assign line_address[CACHE_LINE_SIZE_WIDTH-1:0] = {CACHE_LINE_SIZE_WIDTH{1'b0}};
|
|
|
|
|
//-----------------------------------------------------------------
|
//-----------------------------------------------------------------
|
// Control logic
|
// Next State Logic
|
//-----------------------------------------------------------------
|
//-----------------------------------------------------------------
|
always @ (posedge rst_i or posedge clk_i )
|
reg [3:0] next_state_r;
|
begin
|
always @ *
|
if (rst_i == 1'b1)
|
|
begin
|
|
data_w <= 32'h00000000;
|
|
data_wr <= 4'h0;
|
|
data_rd <= 1'b0;
|
|
req_address <= 32'h00000000;
|
|
req_ack <= 1'b0;
|
|
req_wr <= 4'h0;
|
|
req_rd <= 1'b0;
|
|
tag_wr <= 1'b0;
|
|
req_flush <= 1'b0;
|
|
flush_single <= 1'b0;
|
|
fill <= 1'b0;
|
|
evict <= 1'b0;
|
|
cache_data_w <= 32'h00000000;
|
|
cache_wr <= 4'b0;
|
|
ack <= 1'b0;
|
|
state <= STATE_IDLE;
|
|
end
|
|
else
|
|
begin
|
begin
|
ack <= 1'b0;
|
next_state_r = state;
|
tag_wr <= 1'b0;
|
|
fill <= 1'b0;
|
|
evict <= 1'b0;
|
|
cache_wr <= 4'b0;
|
|
data_wr <= 4'b0;
|
|
data_rd <= 1'b0;
|
|
|
|
if (flush_i)
|
|
req_flush <= 1'b1;
|
|
|
|
case (state)
|
case (state)
|
|
|
//-----------------------------------------
|
//-----------------------------------------
|
// IDLE
|
// IDLE
|
//-----------------------------------------
|
//-----------------------------------------
|
STATE_IDLE :
|
STATE_IDLE :
|
begin
|
begin
|
|
// Cache flush request
|
|
if (flush_i | req_flush)
|
|
next_state_r = STATE_FLUSH2;
|
// Read (uncacheable)
|
// Read (uncacheable)
|
if (rd_i & ~cacheable)
|
else if (stb_i & ~we_i & ~cacheable)
|
begin
|
next_state_r = STATE_SINGLE;
|
// Start read single from memory
|
|
req_address <= address_i;
|
|
req_address[ADDR_CACHE_BYPASS_BIT] <= 1'b0;
|
|
data_rd <= 1'b1;
|
|
req_rd <= 1'b1;
|
|
req_wr <= 4'b0;
|
|
req_ack <= 1'b1;
|
|
state <= STATE_SINGLE;
|
|
end
|
|
// Read (cacheable)
|
// Read (cacheable)
|
else if (rd_i)
|
else if (stb_i & ~we_i)
|
begin
|
next_state_r = STATE_CHECK;
|
req_address <= address_i;
|
|
req_rd <= 1'b1;
|
|
req_wr <= 4'b0;
|
|
req_ack <= 1'b1;
|
|
state <= STATE_CHECK;
|
|
end
|
|
// Write (uncacheable)
|
// Write (uncacheable)
|
else if (wr_i != 4'b0000 & ~cacheable)
|
else if (stb_i & we_i & ~cacheable)
|
begin
|
next_state_r = STATE_SINGLE;
|
// Perform write single
|
|
req_address <= address_i;
|
|
req_address[ADDR_CACHE_BYPASS_BIT] <= 1'b0;
|
|
data_w <= data_i;
|
|
data_wr <= wr_i;
|
|
req_wr <= wr_i;
|
|
req_rd <= 1'b0;
|
|
req_ack <= 1'b1;
|
|
state <= STATE_SINGLE;
|
|
end
|
|
// Write (cacheable)
|
// Write (cacheable)
|
else if (wr_i != 4'b0000)
|
else if (stb_i & we_i)
|
begin
|
next_state_r = STATE_WRITE;
|
req_address <= address_i;
|
|
data_w <= data_i;
|
|
req_wr <= wr_i;
|
|
req_rd <= 1'b0;
|
|
req_ack <= 1'b0;
|
|
|
|
// Early ACK
|
|
ack <= 1'b1;
|
|
|
|
state <= STATE_WRITE;
|
|
end
|
|
// Cache flush request
|
|
else if (flush_i | req_flush)
|
|
begin
|
|
// Set to first line address
|
|
req_address <= 32'h00000000;
|
|
req_flush <= 1'b0;
|
|
req_ack <= 1'b0;
|
|
flush_single<= 1'b0;
|
|
state <= STATE_FLUSH2;
|
|
end
|
|
end
|
end
|
//-----------------------------------------
|
//-----------------------------------------
|
// WRITE - Wait for write-thru to complete
|
// WRITE - Wait for write-thru to complete
|
//-----------------------------------------
|
//-----------------------------------------
|
STATE_WRITE :
|
STATE_WRITE :
|
begin
|
begin
|
// Cache hit
|
// Cache hit
|
if (valid &&
|
if (valid & addr_hit)
|
(req_address[CACHE_TAG_ADDR_HIGH:CACHE_TAG_ADDR_LOW] == tag_data_out[13:0]))
|
next_state_r = STATE_WAIT2;
|
begin
|
|
// Update line already in cache
|
|
cache_data_w <= data_w;
|
|
cache_wr <= req_wr;
|
|
|
|
// Mark line as dirty
|
|
tag_data_in <= tag_data_out;
|
|
tag_data_in[CACHE_TAG_DIRTY_BIT] <= 1'b1;
|
|
tag_wr <= 1'b1;
|
|
|
|
state <= STATE_WAIT2;
|
|
end
|
|
// Cache dirty
|
// Cache dirty
|
else if (valid & dirty)
|
else if (valid & dirty)
|
begin
|
next_state_r = STATE_EVICTING;
|
// Evict cache line
|
|
evict <= 1'b1;
|
|
state <= STATE_EVICTING;
|
|
end
|
|
// Cache miss
|
// Cache miss
|
else
|
else
|
begin
|
next_state_r = STATE_UPDATE;
|
// Update tag memory with this line's details
|
|
tag_data_in <= {1'b1, 1'b1, req_address[CACHE_TAG_ADDR_HIGH:CACHE_TAG_ADDR_LOW]};
|
|
tag_wr <= 1'b1;
|
|
|
|
// Fill cache line
|
|
fill <= 1'b1;
|
|
state <= STATE_UPDATE;
|
|
end
|
|
end
|
end
|
//-----------------------------------------
|
//-----------------------------------------
|
// EVICTING - Evicting cache line
|
// EVICTING - Evicting cache line
|
//-----------------------------------------
|
//-----------------------------------------
|
STATE_EVICTING:
|
STATE_EVICTING:
|
begin
|
begin
|
// Data ready from memory?
|
// Data ready from memory?
|
if (done)
|
if (done)
|
begin
|
begin
|
// Update tag memory with this new line's details
|
|
tag_data_in <= {1'b1, 1'b0, req_address[CACHE_TAG_ADDR_HIGH:CACHE_TAG_ADDR_LOW]};
|
|
tag_wr <= 1'b1;
|
|
|
|
// Fill cache line
|
|
fill <= 1'b1;
|
|
|
|
// Evict for read?
|
// Evict for read?
|
if (req_rd)
|
if (req_rd)
|
state <= STATE_FETCH;
|
next_state_r = STATE_FETCH;
|
// Evict for write
|
// Evict for write
|
else
|
else
|
state <= STATE_UPDATE;
|
next_state_r = STATE_UPDATE;
|
end
|
end
|
end
|
end
|
//-----------------------------------------
|
//-----------------------------------------
|
// UPDATE - Update fetched cache line
|
// UPDATE - Update fetched cache line
|
//-----------------------------------------
|
//-----------------------------------------
|
STATE_UPDATE:
|
STATE_UPDATE:
|
begin
|
begin
|
// Data ready from memory?
|
// Data ready from memory?
|
if (done)
|
if (done)
|
begin
|
next_state_r = STATE_WAIT2;
|
// Update line already in cache
|
|
cache_data_w <= data_w;
|
|
cache_wr <= req_wr;
|
|
|
|
// Mark line as dirty
|
|
tag_data_in <= tag_data_out;
|
|
tag_data_in[CACHE_TAG_DIRTY_BIT] <= 1'b1;
|
|
tag_wr <= 1'b1;
|
|
|
|
state <= STATE_WAIT2;
|
|
end
|
|
end
|
end
|
//-----------------------------------------
|
//-----------------------------------------
|
// CHECK - check cache for hit or miss
|
// CHECK - check cache for hit or miss
|
//-----------------------------------------
|
//-----------------------------------------
|
STATE_CHECK :
|
STATE_CHECK :
|
begin
|
begin
|
// Cache hit
|
// Cache hit
|
if (valid &&
|
if (valid & addr_hit)
|
(req_address[CACHE_TAG_ADDR_HIGH:CACHE_TAG_ADDR_LOW] == tag_data_out[13:0]))
|
next_state_r = STATE_IDLE;
|
begin
|
|
state <= STATE_IDLE;
|
|
end
|
|
// Cache dirty
|
// Cache dirty
|
else if (valid & dirty)
|
else if (valid & dirty)
|
begin
|
next_state_r = STATE_EVICTING;
|
// Evict cache line
|
|
evict <= 1'b1;
|
|
state <= STATE_EVICTING;
|
|
end
|
|
// Cache miss
|
// Cache miss
|
else
|
else
|
begin
|
next_state_r = STATE_FETCH;
|
// Update tag memory with this line's details
|
|
tag_data_in <= {1'b1, 1'b0, req_address[CACHE_TAG_ADDR_HIGH:CACHE_TAG_ADDR_LOW]};
|
|
tag_wr <= 1'b1;
|
|
|
|
// Fill cache line
|
|
fill <= 1'b1;
|
|
state <= STATE_FETCH;
|
|
end
|
|
end
|
end
|
//-----------------------------------------
|
//-----------------------------------------
|
// FETCH_SINGLE - Single access to memory
|
// FETCH_SINGLE - Single access to memory
|
//-----------------------------------------
|
//-----------------------------------------
|
STATE_SINGLE:
|
STATE_SINGLE:
|
begin
|
begin
|
// Data ready from memory?
|
// Data ready from memory?
|
if (done)
|
if (done)
|
begin
|
begin
|
// Single WRITE?
|
// Single WRITE?
|
if (~req_rd)
|
if (~req_rd)
|
begin
|
next_state_r = STATE_SINGLE_READY;
|
state <= STATE_SINGLE_READY;
|
|
ack <= req_ack;
|
|
end
|
|
// Dirty? Write back
|
// Dirty? Write back
|
else if (valid & dirty)
|
else if (valid & dirty & addr_hit)
|
begin
|
next_state_r = STATE_FLUSH4;
|
// Evict cache line
|
|
evict <= 1'b1;
|
|
flush_single<= 1'b1;
|
|
state <= STATE_FLUSH4;
|
|
end
|
|
// Valid line, invalidate
|
// Valid line, invalidate
|
else if (valid)
|
else if (valid & addr_hit)
|
begin
|
next_state_r = STATE_SINGLE_READY;
|
tag_data_in <= tag_data_out;
|
|
tag_data_in[CACHE_TAG_VALID_BIT] <= 1'b0;
|
|
tag_wr <= 1'b1;
|
|
|
|
state <= STATE_SINGLE_READY;
|
|
ack <= req_ack;
|
|
end
|
|
else
|
else
|
begin
|
next_state_r = STATE_SINGLE_READY;
|
state <= STATE_SINGLE_READY;
|
|
ack <= req_ack;
|
|
end
|
|
end
|
end
|
end
|
end
|
//-----------------------------------------
|
//-----------------------------------------
|
// FETCH - Fetch row from memory
|
// FETCH - Fetch row from memory
|
//-----------------------------------------
|
//-----------------------------------------
|
STATE_FETCH :
|
STATE_FETCH :
|
begin
|
begin
|
// Cache line filled?
|
// Cache line filled?
|
if (done)
|
if (done)
|
state <= STATE_WAIT;
|
next_state_r = STATE_WAIT;
|
end
|
end
|
//-----------------------------------------
|
//-----------------------------------------
|
// WAIT - Wait cycle
|
// WAIT - Wait cycle
|
//-----------------------------------------
|
//-----------------------------------------
|
STATE_WAIT :
|
STATE_WAIT :
|
begin
|
begin
|
// Allow extra wait state to handle write & read collision
|
// Allow extra wait state to handle write & read collision
|
state <= STATE_WAIT2;
|
next_state_r = STATE_WAIT2;
|
end
|
end
|
//-----------------------------------------
|
//-----------------------------------------
|
// WAIT2 - Wait cycle
|
// WAIT2 - Wait cycle
|
//-----------------------------------------
|
//-----------------------------------------
|
STATE_WAIT2 :
|
STATE_WAIT2 :
|
begin
|
begin
|
state <= STATE_IDLE;
|
next_state_r = STATE_IDLE;
|
ack <= req_ack;
|
|
end
|
end
|
//-----------------------------------------
|
//-----------------------------------------
|
// SINGLE_READY - Uncached access ready
|
// SINGLE_READY - Uncached access ready
|
//-----------------------------------------
|
//-----------------------------------------
|
STATE_SINGLE_READY :
|
STATE_SINGLE_READY :
|
begin
|
begin
|
// Allow extra wait state to handle write & read collision
|
// Allow extra wait state to handle write & read collision
|
state <= STATE_IDLE;
|
next_state_r = STATE_IDLE;
|
end
|
end
|
//-----------------------------------------
|
//-----------------------------------------
|
// FLUSHx - Flush dirty lines & invalidate
|
// FLUSHx - Flush dirty lines & invalidate
|
//-----------------------------------------
|
//-----------------------------------------
|
STATE_FLUSH1 :
|
STATE_FLUSH1 :
|
begin
|
begin
|
if (req_address[CACHE_LINE_ADDR_WIDTH + CACHE_LINE_SIZE_WIDTH - 1:CACHE_LINE_SIZE_WIDTH] == {CACHE_LINE_ADDR_WIDTH{1'b1}})
|
if (req_address[CACHE_LINE_ADDR_WIDTH + CACHE_LINE_SIZE_WIDTH - 1:CACHE_LINE_SIZE_WIDTH] == {CACHE_LINE_ADDR_WIDTH{1'b1}})
|
begin
|
next_state_r = STATE_WAIT;
|
req_ack <= 1'b0;
|
|
state <= STATE_WAIT;
|
|
end
|
|
else
|
else
|
begin
|
next_state_r = STATE_FLUSH2;
|
// Increment flush line address
|
|
req_address[CACHE_LINE_ADDR_WIDTH + CACHE_LINE_SIZE_WIDTH - 1:CACHE_LINE_SIZE_WIDTH] <=
|
|
req_address[CACHE_LINE_ADDR_WIDTH + CACHE_LINE_SIZE_WIDTH - 1:CACHE_LINE_SIZE_WIDTH] + 1;
|
|
|
|
state <= STATE_FLUSH2;
|
|
end
|
|
end
|
end
|
//-----------------------------------------
|
//-----------------------------------------
|
// FLUSH2 - Wait state
|
// FLUSH2 - Wait state
|
//-----------------------------------------
|
//-----------------------------------------
|
STATE_FLUSH2 :
|
STATE_FLUSH2 :
|
begin
|
begin
|
// Allow a cycle to read line state
|
// Allow a cycle to read line state
|
state <= STATE_FLUSH3;
|
next_state_r = STATE_FLUSH3;
|
end
|
end
|
//-----------------------------------------
|
//-----------------------------------------
|
// FLUSH3 - Check if line dirty & flush
|
// FLUSH3 - Check if line dirty & flush
|
//-----------------------------------------
|
//-----------------------------------------
|
STATE_FLUSH3 :
|
STATE_FLUSH3 :
|
begin
|
begin
|
// Dirty line? Evict line first
|
// Dirty line? Evict line first
|
if (dirty)
|
if (dirty)
|
begin
|
next_state_r = STATE_FLUSH4;
|
// Evict cache line
|
|
evict <= 1'b1;
|
|
state <= STATE_FLUSH4;
|
|
end
|
|
// Not dirty? Just invalidate
|
// Not dirty? Just invalidate
|
else
|
else
|
begin
|
begin
|
tag_data_in <= tag_data_out;
|
|
tag_data_in[CACHE_TAG_VALID_BIT] <= 1'b0;
|
|
tag_wr <= 1'b1;
|
|
|
|
if (flush_single)
|
if (flush_single)
|
state <= STATE_WAIT;
|
next_state_r = STATE_WAIT;
|
else
|
else
|
state <= STATE_FLUSH1;
|
next_state_r = STATE_FLUSH1;
|
end
|
end
|
end
|
end
|
//-----------------------------------------
|
//-----------------------------------------
|
// FLUSH4 - Wait for line flush to complete
|
// FLUSH4 - Wait for line flush to complete
|
//-----------------------------------------
|
//-----------------------------------------
|
STATE_FLUSH4 :
|
STATE_FLUSH4 :
|
begin
|
begin
|
// Cache line filled?
|
// Cache line filled?
|
if (done)
|
if (done)
|
begin
|
begin
|
// Invalidate line
|
if (flush_single)
|
|
next_state_r = STATE_SINGLE_READY;
|
|
else
|
|
next_state_r = STATE_FLUSH1;
|
|
end
|
|
end
|
|
|
|
default:
|
|
;
|
|
endcase
|
|
end
|
|
|
|
// Update state
|
|
always @ (posedge rst_i or posedge clk_i )
|
|
begin
|
|
if (rst_i == 1'b1)
|
|
state <= STATE_IDLE;
|
|
else
|
|
state <= next_state_r;
|
|
end
|
|
|
|
//-----------------------------------------------------------------
|
|
// Tag Write
|
|
//-----------------------------------------------------------------
|
|
always @ (posedge rst_i or posedge clk_i )
|
|
begin
|
|
if (rst_i == 1'b1)
|
|
begin
|
|
tag_data_in <= 16'b0;
|
|
tag_wr <= 1'b0;
|
|
end
|
|
else
|
|
begin
|
|
tag_wr <= 1'b0;
|
|
|
|
case (state)
|
|
//-----------------------------------------
|
|
// WRITE - Wait for write-thru to complete
|
|
//-----------------------------------------
|
|
STATE_WRITE :
|
|
begin
|
|
// Cache hit
|
|
if (valid & addr_hit)
|
|
begin
|
|
// Mark line as dirty
|
tag_data_in <= tag_data_out;
|
tag_data_in <= tag_data_out;
|
tag_data_in[CACHE_TAG_VALID_BIT] <= 1'b0;
|
tag_data_in[CACHE_TAG_DIRTY_BIT] <= 1'b1;
|
tag_data_in[CACHE_TAG_DIRTY_BIT] <= 1'b0;
|
tag_wr <= 1'b1;
|
|
end
|
|
// Cache miss / cache line doesn't require write back
|
|
else if (~valid | ~dirty)
|
|
begin
|
|
// Update tag memory with this line's details
|
|
tag_data_in <= {1'b1, 1'b1, req_address[CACHE_TAG_ADDR_HIGH:CACHE_TAG_ADDR_LOW]};
|
|
tag_wr <= 1'b1;
|
|
end
|
|
end
|
|
//-----------------------------------------
|
|
// EVICTING - Evicting cache line
|
|
//-----------------------------------------
|
|
STATE_EVICTING:
|
|
begin
|
|
// Data ready from memory?
|
|
if (done)
|
|
begin
|
|
// Update tag memory with this new line's details
|
|
tag_data_in <= {1'b1, 1'b0, req_address[CACHE_TAG_ADDR_HIGH:CACHE_TAG_ADDR_LOW]};
|
|
tag_wr <= 1'b1;
|
|
end
|
|
end
|
|
//-----------------------------------------
|
|
// UPDATE - Update fetched cache line
|
|
//-----------------------------------------
|
|
STATE_UPDATE:
|
|
begin
|
|
// Data ready from memory?
|
|
if (done)
|
|
begin
|
|
// Mark line as dirty
|
|
tag_data_in <= tag_data_out;
|
|
tag_data_in[CACHE_TAG_DIRTY_BIT] <= 1'b1;
|
tag_wr <= 1'b1;
|
tag_wr <= 1'b1;
|
|
end
|
|
end
|
|
//-----------------------------------------
|
|
// CHECK - check cache for hit or miss
|
|
//-----------------------------------------
|
|
STATE_CHECK :
|
|
begin
|
|
// Cache hit
|
|
if (valid & addr_hit)
|
|
begin
|
|
|
if (flush_single)
|
end
|
|
// Cache miss / cache line doesn't require write back
|
|
else if (~valid | ~dirty)
|
begin
|
begin
|
state <= STATE_SINGLE_READY;
|
// Update tag memory with this line's details
|
ack <= req_ack;
|
tag_data_in <= {1'b1, 1'b0, req_address[CACHE_TAG_ADDR_HIGH:CACHE_TAG_ADDR_LOW]};
|
|
tag_wr <= 1'b1;
|
end
|
end
|
else
|
|
state <= STATE_FLUSH1;
|
|
end
|
end
|
|
//-----------------------------------------
|
|
// FETCH_SINGLE - Single access to memory
|
|
//-----------------------------------------
|
|
STATE_SINGLE:
|
|
begin
|
|
// Data ready from memory?
|
|
if (done)
|
|
begin
|
|
// Single WRITE?
|
|
if (~req_rd)
|
|
begin
|
|
// Invalidate cached version
|
|
if (valid & addr_hit)
|
|
begin
|
|
tag_data_in <= tag_data_out;
|
|
tag_data_in[CACHE_TAG_VALID_BIT] <= 1'b0;
|
|
tag_wr <= 1'b1;
|
|
end
|
|
end
|
|
// Valid line (not dirty), just invalidate
|
|
else if (valid & ~dirty & addr_hit)
|
|
begin
|
|
tag_data_in <= tag_data_out;
|
|
tag_data_in[CACHE_TAG_VALID_BIT] <= 1'b0;
|
|
tag_wr <= 1'b1;
|
|
end
|
|
end
|
|
end
|
|
|
|
//-----------------------------------------
|
|
// FLUSH3 - Check if line dirty & flush
|
|
//-----------------------------------------
|
|
STATE_FLUSH3 :
|
|
begin
|
|
// Not dirty? Just invalidate
|
|
if (~dirty)
|
|
begin
|
|
tag_data_in <= tag_data_out;
|
|
tag_data_in[CACHE_TAG_VALID_BIT] <= 1'b0;
|
|
tag_wr <= 1'b1;
|
|
end
|
|
end
|
|
//-----------------------------------------
|
|
// FLUSH4 - Wait for line flush to complete
|
|
//-----------------------------------------
|
|
STATE_FLUSH4 :
|
|
begin
|
|
// Cache line filled?
|
|
if (done)
|
|
begin
|
|
// Invalidate line
|
|
tag_data_in <= tag_data_out;
|
|
tag_data_in[CACHE_TAG_VALID_BIT] <= 1'b0;
|
|
tag_data_in[CACHE_TAG_DIRTY_BIT] <= 1'b0;
|
|
tag_wr <= 1'b1;
|
|
end
|
|
end
|
|
default:
|
|
;
|
|
endcase
|
|
end
|
|
end
|
|
|
|
//-----------------------------------------------------------------
|
|
// Register requests
|
|
//-----------------------------------------------------------------
|
|
always @ (posedge rst_i or posedge clk_i )
|
|
begin
|
|
if (rst_i == 1'b1)
|
|
begin
|
|
req_address <= 32'h00000000;
|
|
req_data <= 32'h00000000;
|
|
req_ack <= 1'b0;
|
|
req_wr <= 4'h0;
|
|
req_rd <= 1'b0;
|
|
req_flush <= 1'b0;
|
|
end
|
|
else
|
|
begin
|
|
if (flush_i)
|
|
req_flush <= 1'b1;
|
|
|
|
case (state)
|
|
//-----------------------------------------
|
|
// IDLE
|
|
//-----------------------------------------
|
|
STATE_IDLE :
|
|
begin
|
|
// Cache flush request
|
|
if (flush_i | req_flush)
|
|
begin
|
|
// Set to first line address
|
|
req_address <= 32'h00000000;
|
|
req_flush <= 1'b0;
|
|
req_ack <= 1'b0;
|
|
end
|
|
// Read (uncacheable)
|
|
else if (stb_i & ~we_i & ~cacheable)
|
|
begin
|
|
// Start read single from memory
|
|
req_address <= address_i;
|
|
req_address[ADDR_CACHE_BYPASS_BIT] <= 1'b0;
|
|
req_rd <= 1'b1;
|
|
req_wr <= 4'b0;
|
|
req_ack <= 1'b1;
|
|
end
|
|
// Read (cacheable)
|
|
else if (stb_i & ~we_i)
|
|
begin
|
|
req_address <= address_i;
|
|
req_rd <= 1'b1;
|
|
req_wr <= 4'b0;
|
|
req_ack <= 1'b1;
|
|
end
|
|
// Write (uncacheable)
|
|
else if (stb_i & we_i & ~cacheable)
|
|
begin
|
|
// Perform write single
|
|
req_address <= address_i;
|
|
req_address[ADDR_CACHE_BYPASS_BIT] <= 1'b0;
|
|
req_data <= data_i;
|
|
req_wr <= sel_i;
|
|
req_rd <= 1'b0;
|
|
req_ack <= 1'b1;
|
|
end
|
|
// Write (cacheable)
|
|
else if (stb_i & we_i)
|
|
begin
|
|
req_address <= address_i;
|
|
req_data <= data_i;
|
|
req_wr <= sel_i;
|
|
req_rd <= 1'b0;
|
|
req_ack <= 1'b0;
|
|
end
|
|
end
|
|
//-----------------------------------------
|
|
// FLUSHx - Flush dirty lines & invalidate
|
|
//-----------------------------------------
|
|
STATE_FLUSH1 :
|
|
begin
|
|
if (req_address[CACHE_LINE_ADDR_WIDTH + CACHE_LINE_SIZE_WIDTH - 1:CACHE_LINE_SIZE_WIDTH] == {CACHE_LINE_ADDR_WIDTH{1'b1}})
|
|
begin
|
|
req_ack <= 1'b0;
|
|
end
|
|
else
|
|
begin
|
|
// Increment flush line address
|
|
req_address[CACHE_LINE_ADDR_WIDTH + CACHE_LINE_SIZE_WIDTH - 1:CACHE_LINE_SIZE_WIDTH] <=
|
|
req_address[CACHE_LINE_ADDR_WIDTH + CACHE_LINE_SIZE_WIDTH - 1:CACHE_LINE_SIZE_WIDTH] + 1;
|
|
end
|
|
end
|
|
default:
|
|
;
|
|
endcase
|
|
end
|
|
end
|
|
|
|
//-----------------------------------------------------------------
|
|
// Cache Data Write
|
|
//-----------------------------------------------------------------
|
|
always @ (posedge rst_i or posedge clk_i )
|
|
begin
|
|
if (rst_i == 1'b1)
|
|
begin
|
|
cache_data_w <= 32'h00000000;
|
|
cache_wr <= 4'b0;
|
|
end
|
|
else
|
|
begin
|
|
cache_wr <= 4'b0;
|
|
|
|
case (state)
|
|
//-----------------------------------------
|
|
// WRITE - Wait for write-thru to complete
|
|
//-----------------------------------------
|
|
STATE_WRITE :
|
|
begin
|
|
// Cache hit
|
|
if (valid & addr_hit)
|
|
begin
|
|
// Update line already in cache
|
|
cache_data_w <= req_data;
|
|
cache_wr <= req_wr;
|
|
end
|
|
end
|
|
//-----------------------------------------
|
|
// UPDATE - Update fetched cache line
|
|
//-----------------------------------------
|
|
STATE_UPDATE:
|
|
begin
|
|
// Data ready from memory?
|
|
if (done)
|
|
begin
|
|
// Update line already in cache
|
|
cache_data_w <= req_data;
|
|
cache_wr <= req_wr;
|
|
end
|
|
end
|
|
default:
|
|
;
|
|
endcase
|
|
end
|
|
end
|
|
|
|
//-----------------------------------------------------------------
|
|
// Control
|
|
//-----------------------------------------------------------------
|
|
always @ (posedge rst_i or posedge clk_i )
|
|
begin
|
|
if (rst_i == 1'b1)
|
|
begin
|
|
wr_single <= 4'h0;
|
|
rd_single <= 1'b0;
|
|
flush_single <= 1'b0;
|
|
fill <= 1'b0;
|
|
evict <= 1'b0;
|
|
end
|
|
else
|
|
begin
|
|
fill <= 1'b0;
|
|
evict <= 1'b0;
|
|
wr_single <= 4'b0;
|
|
rd_single <= 1'b0;
|
|
|
|
case (state)
|
|
|
|
//-----------------------------------------
|
|
// IDLE
|
|
//-----------------------------------------
|
|
STATE_IDLE :
|
|
begin
|
|
// Cache flush request
|
|
if (flush_i | req_flush)
|
|
begin
|
|
// Set to first line address
|
|
flush_single<= 1'b0;
|
|
end
|
|
// Read (uncacheable)
|
|
else if (stb_i & ~we_i & ~cacheable)
|
|
begin
|
|
// Start read single from memory
|
|
rd_single <= 1'b1;
|
|
end
|
|
// Write (uncacheable)
|
|
else if (stb_i & we_i & ~cacheable)
|
|
begin
|
|
// Perform write single
|
|
wr_single <= sel_i;
|
|
end
|
|
end
|
|
//-----------------------------------------
|
|
// WRITE - Wait for write-thru to complete
|
|
//-----------------------------------------
|
|
STATE_WRITE :
|
|
begin
|
|
// Cache hit
|
|
if (valid & addr_hit)
|
|
begin
|
|
|
|
end
|
|
// Cache dirty
|
|
else if (valid & dirty)
|
|
begin
|
|
// Evict cache line
|
|
evict <= 1'b1;
|
|
end
|
|
// Cache miss
|
|
else
|
|
begin
|
|
// Fill cache line
|
|
fill <= 1'b1;
|
|
end
|
|
end
|
|
//-----------------------------------------
|
|
// EVICTING - Evicting cache line
|
|
//-----------------------------------------
|
|
STATE_EVICTING:
|
|
begin
|
|
// Data ready from memory?
|
|
if (done)
|
|
begin
|
|
// Fill cache line
|
|
fill <= 1'b1;
|
|
end
|
|
end
|
|
//-----------------------------------------
|
|
// CHECK - check cache for hit or miss
|
|
//-----------------------------------------
|
|
STATE_CHECK :
|
|
begin
|
|
// Cache hit
|
|
if (valid & addr_hit)
|
|
begin
|
|
|
|
end
|
|
// Cache dirty
|
|
else if (valid & dirty)
|
|
begin
|
|
// Evict cache line
|
|
evict <= 1'b1;
|
|
end
|
|
// Cache miss
|
|
else
|
|
begin
|
|
// Fill cache line
|
|
fill <= 1'b1;
|
|
end
|
|
end
|
|
//-----------------------------------------
|
|
// FETCH_SINGLE - Single access to memory
|
|
//-----------------------------------------
|
|
STATE_SINGLE:
|
|
begin
|
|
// Data ready from memory?
|
|
if (done)
|
|
begin
|
|
// Single WRITE?
|
|
if (~req_rd)
|
|
begin
|
|
|
|
end
|
|
// Dirty? Write back
|
|
else if (valid & dirty & addr_hit)
|
|
begin
|
|
// Evict cache line
|
|
evict <= 1'b1;
|
|
flush_single<= 1'b1;
|
|
end
|
|
end
|
|
end
|
|
//-----------------------------------------
|
|
// FLUSH3 - Check if line dirty & flush
|
|
//-----------------------------------------
|
|
STATE_FLUSH3 :
|
|
begin
|
|
// Dirty line? Evict line first
|
|
if (dirty)
|
|
begin
|
|
// Evict cache line
|
|
evict <= 1'b1;
|
|
end
|
|
end
|
|
default:
|
|
;
|
|
endcase
|
|
end
|
|
end
|
|
|
|
//-----------------------------------------------------------------
|
|
// ACK
|
|
//-----------------------------------------------------------------
|
|
always @ (posedge rst_i or posedge clk_i )
|
|
begin
|
|
if (rst_i == 1'b1)
|
|
ack <= 1'b0;
|
|
else
|
|
begin
|
|
ack <= 1'b0;
|
|
|
|
case (state)
|
|
|
|
//-----------------------------------------
|
|
// IDLE
|
|
//-----------------------------------------
|
|
STATE_IDLE :
|
|
begin
|
|
// Write (cacheable), early acknowledge
|
|
if (~(flush_i | req_flush) & stb_i & we_i & cacheable)
|
|
ack <= 1'b1;
|
|
end
|
|
//-----------------------------------------
|
|
// FETCH_SINGLE - Single access to memory
|
|
//-----------------------------------------
|
|
STATE_SINGLE:
|
|
begin
|
|
// Data ready from memory?
|
|
if (done)
|
|
begin
|
|
// Single WRITE?
|
|
if (~req_rd)
|
|
ack <= req_ack;
|
|
// Dirty? Write back
|
|
else if (valid & dirty & addr_hit)
|
|
begin
|
|
|
|
end
|
|
// Valid line, invalidate
|
|
else if (valid & addr_hit)
|
|
ack <= req_ack;
|
|
else
|
|
ack <= req_ack;
|
|
end
|
|
end
|
|
//-----------------------------------------
|
|
// WAIT2 - Wait cycle
|
|
//-----------------------------------------
|
|
STATE_WAIT2 :
|
|
begin
|
|
ack <= req_ack;
|
|
end
|
|
//-----------------------------------------
|
|
// FLUSH4 - Wait for line flush to complete
|
|
//-----------------------------------------
|
|
STATE_FLUSH4 :
|
|
begin
|
|
if (done & flush_single)
|
|
ack <= req_ack;
|
end
|
end
|
default:
|
default:
|
;
|
;
|
endcase
|
endcase
|
end
|
end
|
end
|
end
|
|
|
//-----------------------------------------------------------------
|
//-----------------------------------------------------------------
|
// Instantiation
|
// Instantiation
|
//-----------------------------------------------------------------
|
//-----------------------------------------------------------------
|
|
|
altor32_dcache_mem_if
|
altor32_dcache_mem_if
|
#(
|
#(
|
.CACHE_LINE_SIZE_WIDTH(CACHE_LINE_SIZE_WIDTH),
|
.CACHE_LINE_SIZE_WIDTH(CACHE_LINE_SIZE_WIDTH),
|
.CACHE_LINE_WORDS_IDX_MAX(CACHE_LINE_WORDS_IDX_MAX)
|
.CACHE_LINE_WORDS_IDX_MAX(CACHE_LINE_WORDS_IDX_MAX)
|
)
|
)
|
u_mem_if
|
u_mem_if
|
(
|
(
|
.clk_i(clk_i),
|
.clk_i(clk_i),
|
.rst_i(rst_i),
|
.rst_i(rst_i),
|
|
|
// Cache interface
|
// Cache interface
|
.address_i(muxed_address),
|
.address_i(muxed_address),
|
.data_i(data_w),
|
.data_i(req_data),
|
.data_o(data_r),
|
.data_o(data_r),
|
.fill_i(fill),
|
.fill_i(fill),
|
.evict_i(evict),
|
.evict_i(evict),
|
.evict_addr_i(line_address),
|
.evict_addr_i(line_address),
|
.rd_single_i(data_rd),
|
.rd_single_i(rd_single),
|
.wr_single_i(data_wr),
|
.wr_single_i(wr_single),
|
.done_o(done),
|
.done_o(done),
|
|
|
// Cache memory (fill/evict)
|
// Cache memory (fill/evict)
|
.cache_addr_o(cache_update_addr),
|
.cache_addr_o(cache_update_addr),
|
.cache_data_o(cache_update_data_w),
|
.cache_data_o(cache_update_data_w),
|
.cache_data_i(cache_update_data_r),
|
.cache_data_i(cache_update_data_r),
|
.cache_wr_o(cache_update_wr),
|
.cache_wr_o(cache_update_wr),
|
|
|
// Memory interface (slave)
|
// Memory interface (slave)
|
.mem_addr_o(mem_addr_o),
|
.mem_addr_o(mem_addr_o),
|
.mem_data_i(mem_data_i),
|
.mem_data_i(mem_data_i),
|
.mem_data_o(mem_data_o),
|
.mem_data_o(mem_data_o),
|
.mem_burst_o(mem_burst_o),
|
.mem_cti_o(mem_cti_o),
|
.mem_rd_o(mem_rd_o),
|
.mem_cyc_o(mem_cyc_o),
|
.mem_wr_o(mem_wr_o),
|
.mem_stb_o(mem_stb_o),
|
.mem_accept_i(mem_accept_i),
|
.mem_we_o(mem_we_o),
|
|
.mem_sel_o(mem_sel_o),
|
|
.mem_stall_i(mem_stall_i),
|
.mem_ack_i(mem_ack_i)
|
.mem_ack_i(mem_ack_i)
|
);
|
);
|
|
|
// Tag memory
|
// Tag memory
|
altor32_ram_sp
|
altor32_ram_sp
|
#(
|
#(
|
.WIDTH(CACHE_TAG_WIDTH),
|
.WIDTH(CACHE_TAG_WIDTH),
|
.SIZE(CACHE_LINE_ADDR_WIDTH)
|
.SIZE(CACHE_LINE_ADDR_WIDTH)
|
)
|
)
|
u1_tag_mem
|
u1_tag_mem
|
(
|
(
|
.clk_i(clk_i),
|
.clk_i(clk_i),
|
.dat_o(tag_data_out),
|
.dat_o(tag_data_out),
|
.dat_i(tag_data_in),
|
.dat_i(tag_data_in),
|
.adr_i(tag_entry),
|
.adr_i(tag_entry),
|
.wr_i(tag_wr)
|
.wr_i(tag_wr)
|
);
|
);
|
|
|
// Data memory
|
// Data memory
|
altor32_ram_dp
|
altor32_ram_dp
|
#(
|
#(
|
.WIDTH(8),
|
.WIDTH(8),
|
.SIZE(CACHE_DWIDTH)
|
.SIZE(CACHE_DWIDTH)
|
)
|
)
|
u2_data_mem0
|
u2_data_mem0
|
(
|
(
|
.aclk_i(clk_i),
|
.aclk_i(clk_i),
|
.aadr_i(cache_address),
|
.aadr_i(cache_address),
|
.adat_o(cache_data_r[7:0]),
|
.adat_o(cache_data_r[7:0]),
|
.adat_i(cache_data_w[7:0]),
|
.adat_i(cache_data_w[7:0]),
|
.awr_i(cache_wr[0]),
|
.awr_i(cache_wr[0]),
|
|
|
.bclk_i(clk_i),
|
.bclk_i(clk_i),
|
.badr_i(cache_update_addr[CACHE_DWIDTH+2-1:2]),
|
.badr_i(cache_update_addr[CACHE_DWIDTH+2-1:2]),
|
.bdat_o(cache_update_data_r[7:0]),
|
.bdat_o(cache_update_data_r[7:0]),
|
.bdat_i(cache_update_data_w[7:0]),
|
.bdat_i(cache_update_data_w[7:0]),
|
.bwr_i(cache_update_wr)
|
.bwr_i(cache_update_wr)
|
);
|
);
|
|
|
altor32_ram_dp
|
altor32_ram_dp
|
#(
|
#(
|
.WIDTH(8),
|
.WIDTH(8),
|
.SIZE(CACHE_DWIDTH)
|
.SIZE(CACHE_DWIDTH)
|
)
|
)
|
u2_data_mem1
|
u2_data_mem1
|
(
|
(
|
.aclk_i(clk_i),
|
.aclk_i(clk_i),
|
.aadr_i(cache_address),
|
.aadr_i(cache_address),
|
.adat_o(cache_data_r[15:8]),
|
.adat_o(cache_data_r[15:8]),
|
.adat_i(cache_data_w[15:8]),
|
.adat_i(cache_data_w[15:8]),
|
.awr_i(cache_wr[1]),
|
.awr_i(cache_wr[1]),
|
|
|
.bclk_i(clk_i),
|
.bclk_i(clk_i),
|
.badr_i(cache_update_addr[CACHE_DWIDTH+2-1:2]),
|
.badr_i(cache_update_addr[CACHE_DWIDTH+2-1:2]),
|
.bdat_o(cache_update_data_r[15:8]),
|
.bdat_o(cache_update_data_r[15:8]),
|
.bdat_i(cache_update_data_w[15:8]),
|
.bdat_i(cache_update_data_w[15:8]),
|
.bwr_i(cache_update_wr)
|
.bwr_i(cache_update_wr)
|
);
|
);
|
|
|
altor32_ram_dp
|
altor32_ram_dp
|
#(
|
#(
|
.WIDTH(8),
|
.WIDTH(8),
|
.SIZE(CACHE_DWIDTH)
|
.SIZE(CACHE_DWIDTH)
|
)
|
)
|
u2_data_mem2
|
u2_data_mem2
|
(
|
(
|
.aclk_i(clk_i),
|
.aclk_i(clk_i),
|
.aadr_i(cache_address),
|
.aadr_i(cache_address),
|
.adat_o(cache_data_r[23:16]),
|
.adat_o(cache_data_r[23:16]),
|
.adat_i(cache_data_w[23:16]),
|
.adat_i(cache_data_w[23:16]),
|
.awr_i(cache_wr[2]),
|
.awr_i(cache_wr[2]),
|
|
|
.bclk_i(clk_i),
|
.bclk_i(clk_i),
|
.badr_i(cache_update_addr[CACHE_DWIDTH+2-1:2]),
|
.badr_i(cache_update_addr[CACHE_DWIDTH+2-1:2]),
|
.bdat_o(cache_update_data_r[23:16]),
|
.bdat_o(cache_update_data_r[23:16]),
|
.bdat_i(cache_update_data_w[23:16]),
|
.bdat_i(cache_update_data_w[23:16]),
|
.bwr_i(cache_update_wr)
|
.bwr_i(cache_update_wr)
|
);
|
);
|
|
|
altor32_ram_dp
|
altor32_ram_dp
|
#(
|
#(
|
.WIDTH(8),
|
.WIDTH(8),
|
.SIZE(CACHE_DWIDTH)
|
.SIZE(CACHE_DWIDTH)
|
)
|
)
|
u2_data_mem3
|
u2_data_mem3
|
(
|
(
|
.aclk_i(clk_i),
|
.aclk_i(clk_i),
|
.aadr_i(cache_address),
|
.aadr_i(cache_address),
|
.adat_o(cache_data_r[31:24]),
|
.adat_o(cache_data_r[31:24]),
|
.adat_i(cache_data_w[31:24]),
|
.adat_i(cache_data_w[31:24]),
|
.awr_i(cache_wr[3]),
|
.awr_i(cache_wr[3]),
|
|
|
.bclk_i(clk_i),
|
.bclk_i(clk_i),
|
.badr_i(cache_update_addr[CACHE_DWIDTH+2-1:2]),
|
.badr_i(cache_update_addr[CACHE_DWIDTH+2-1:2]),
|
.bdat_o(cache_update_data_r[31:24]),
|
.bdat_o(cache_update_data_r[31:24]),
|
.bdat_i(cache_update_data_w[31:24]),
|
.bdat_i(cache_update_data_w[31:24]),
|
.bwr_i(cache_update_wr)
|
.bwr_i(cache_update_wr)
|
);
|
);
|
|
|
endmodule
|
endmodule
|
|
|