cen64/vr4300/dcache.c
Tyler J. Stachecki a12c5a3e04 vr4300: Fix a bug in (D) Index Load Tag.
The VALID and DIRTY bits were not being shifted into the
proper positions after reading them from the line states.
2016-07-09 12:39:45 -04:00

172 lines
5.4 KiB
C

//
// vr4300/dcache.c: VR4300 instruction cache.
//
// CEN64: Cycle-Accurate Nintendo 64 Emulator.
// Copyright (C) 2015, Tyler J. Stachecki.
//
// This file is subject to the terms and conditions defined in
// 'LICENSE', which is part of this source code package.
//
#include "common.h"
#include "vr4300/dcache.h"
static inline struct vr4300_dcache_line* get_line(
struct vr4300_dcache *dcache, uint64_t vaddr);
static inline uint32_t get_tag(const struct vr4300_dcache_line *line);
static inline bool is_valid(const struct vr4300_dcache_line *line);
static void invalidate_line(struct vr4300_dcache_line *line);
static bool is_dirty(const struct vr4300_dcache_line *line);
static void set_dirty(struct vr4300_dcache_line *line);
static void set_tag(struct vr4300_dcache_line *line, uint32_t tag);
static void set_taglo(struct vr4300_dcache_line *line, uint32_t taglo);
static void validate_line(struct vr4300_dcache_line *line, uint32_t tag);
// Returns the line for a given virtual address.
struct vr4300_dcache_line* get_line(
struct vr4300_dcache *dcache, uint64_t vaddr) {
return dcache->lines + (vaddr >> 4 & 0x1FF);
}
// Returns the physical tag associated with the line.
uint32_t get_tag(const struct vr4300_dcache_line *line) {
return line->metadata & ~0xFFFU;
}
// Invalidates the line, but leaves the physical tag untouched.
void invalidate_line(struct vr4300_dcache_line *line) {
line->metadata &= ~0x1;
}
// Returns true if the line is valid, otherwise returns false.
bool is_dirty(const struct vr4300_dcache_line *line) {
return (line->metadata & 0x2) == 0x2;
}
// Returns true if the line is valid, otherwise returns false.
bool is_valid(const struct vr4300_dcache_line *line) {
return (line->metadata & 0x1) == 0x1;
}
// Sets the state of the line to dirty.
void set_dirty(struct vr4300_dcache_line *line) {
line->metadata |= 0x2;
}
// Sets the tag of the specified line, retaining current valid bit.
void set_tag(struct vr4300_dcache_line *line, uint32_t tag) {
line->metadata = tag | (line->metadata & 0x1);
}
// Sets the tag of the specified line and valid bit.
void set_taglo(struct vr4300_dcache_line *line, uint32_t taglo) {
line->metadata = (taglo << 4 & 0xFFFFF000U) | (taglo >> 7 & 0x1) | (taglo >> 5 & 0x2);
}
// Sets the line's physical tag and validates the line.
static void validate_line(struct vr4300_dcache_line *line, uint32_t tag) {
line->metadata = tag | 0x1;
}
// Sets the physical tag associated with the line, marks as dirty.
void vr4300_dcache_create_dirty_exclusive(
struct vr4300_dcache *dcache, uint64_t vaddr, uint32_t paddr) {
struct vr4300_dcache_line *line = get_line(dcache, vaddr);
set_tag(line, paddr & ~0xFFFU);
line->metadata |= 0x3;
}
// Fills an instruction cache line with data.
void vr4300_dcache_fill(struct vr4300_dcache *dcache,
uint64_t vaddr, uint32_t paddr, const void *data) {
struct vr4300_dcache_line *line = get_line(dcache, vaddr);
memcpy(line->data, data, sizeof(line->data));
validate_line(line, paddr & ~0xFFFU);
}
// Returns the tag of the line associated with vaddr.
uint32_t vr4300_dcache_get_tag(const struct vr4300_dcache_line *line, uint64_t vaddr) {
return get_tag(line) | (vaddr & 0xFF0U);
}
// Gets the physical tag associated with the line.
uint32_t vr4300_dcache_get_taglo(struct vr4300_dcache *dcache, uint64_t vaddr) {
struct vr4300_dcache_line *line = get_line(dcache, vaddr);
uint32_t taglo = ((line->metadata & 0x1) << 1) | ((line->metadata & 0x2) >> 1);
return (taglo << 6) | (line->metadata >> 4 & 0x0FFFFF00U);
}
// Initializes the instruction cache.
void vr4300_dcache_init(struct vr4300_dcache *dcache) {
}
// Invalidates an instruction cache line (regardless if hit or miss).
void vr4300_dcache_invalidate(struct vr4300_dcache_line *line) {
invalidate_line(line);
}
// Invalidates an instruction cache line (only on a hit).
void vr4300_dcache_invalidate_hit(struct vr4300_dcache *dcache,
uint64_t vaddr, uint32_t paddr) {
struct vr4300_dcache_line *line = get_line(dcache, vaddr);
uint32_t ptag = get_tag(line);
if (ptag == (paddr & ~0xFFFU) && is_valid(line))
invalidate_line(line);
}
// Probes the instruction cache for a matching line.
struct vr4300_dcache_line* vr4300_dcache_probe(
struct vr4300_dcache *dcache, uint64_t vaddr, uint32_t paddr) {
struct vr4300_dcache_line *line = get_line(dcache, vaddr);
uint32_t ptag = get_tag(line);
// Virtually index, and physically tagged.
if (ptag == (paddr & ~0xFFFU) && is_valid(line))
return line;
return NULL;
}
// Marks the line as dirty.
void vr4300_dcache_set_dirty(struct vr4300_dcache_line *line) {
set_dirty(line);
}
// Sets the physical tag associated with the line.
void vr4300_dcache_set_taglo(struct vr4300_dcache *dcache,
uint64_t vaddr, uint32_t taglo) {
struct vr4300_dcache_line *line = get_line(dcache, vaddr);
set_taglo(line, taglo);
}
// Returns the line if it's dirty and valid.
// Call before replacement of writeback entry.
struct vr4300_dcache_line *vr4300_dcache_should_flush_line(
struct vr4300_dcache *dcache, uint64_t vaddr) {
struct vr4300_dcache_line *line = get_line(dcache, vaddr);
return is_dirty(line) && is_valid(line)
? line : NULL;
}
// Writes back the block if the line is valid, then invalidates the line.
struct vr4300_dcache_line *vr4300_dcache_wb_invalidate(
struct vr4300_dcache *dcache, uint64_t vaddr) {
struct vr4300_dcache_line *line = get_line(dcache, vaddr);
if (is_valid(line)) {
invalidate_line(line);
return line;
}
return NULL;
}