Line 47... |
Line 47... |
|
|
// 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
|
Line 117... |
Line 120... |
|
|
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;
|
Line 155... |
Line 158... |
|
|
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:
|
Line 398... |
Line 274... |
// 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
|
Line 557... |
Line 892... |
.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),
|
Line 576... |
Line 911... |
|
|
// 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
|