Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement ll/lld/sc/scd #185

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions vr4300/cp0.c
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ int VR4300_ERET(struct vr4300 *vr4300,
pipeline->fault_present = true;

vr4300->regs[VR4300_CP0_REGISTER_STATUS] = status;
vr4300->regs[VR4300_REGISTER_LLBIT] = 0;

pipeline->icrf_latch.segment = get_segment(icrf_latch->pc, status);
pipeline->exdc_latch.segment = get_default_segment();
Expand Down
3 changes: 2 additions & 1 deletion vr4300/cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,9 @@ enum vr4300_register {
VR4300_REGISTER_CP1_27, VR4300_REGISTER_CP1_28, VR4300_REGISTER_CP1_29,
VR4300_REGISTER_CP1_30, VR4300_REGISTER_CP1_31,

// Miscellanious registers.
// Miscellaneous registers.
VR4300_REGISTER_HI, VR4300_REGISTER_LO,
VR4300_REGISTER_LLBIT,
VR4300_CP1_FCR0, VR4300_CP1_FCR31,

// Pipeline cycle type flag.
Expand Down
6 changes: 4 additions & 2 deletions vr4300/fault.c
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,8 @@ void VR4300_DCM(struct vr4300 *vr4300) {
VR4300_ACCESS_DWORD ? 0x7 : 0x3;

// Service a read.
if (exdc_latch->request.type == VR4300_BUS_REQUEST_READ) {
if (exdc_latch->request.type == VR4300_BUS_REQUEST_READ ||
exdc_latch->request.type == VR4300_BUS_REQUEST_READ_CONDITIONAL) {
unsigned rshiftamt = (8 - request->size) << 3;
unsigned lshiftamt = (paddr & mask) << 3;
uint32_t hiword, loword;
Expand Down Expand Up @@ -328,7 +329,8 @@ void VR4300_DTLB(struct vr4300 *vr4300, unsigned miss, unsigned inv, unsigned mo

// TLB miss/invalid exceptions are either TLBL or TLBS.
if (miss | inv)
type = (exdc_latch->request.type == VR4300_BUS_REQUEST_WRITE) ? 0x3: 0x2;
type = (exdc_latch->request.type == VR4300_BUS_REQUEST_WRITE ||
exdc_latch->request.type == VR4300_BUS_REQUEST_WRITE_CONDITIONAL) ? 0x3: 0x2;

// OTOH, TLB modification exceptions are TLBM.
else
Expand Down
72 changes: 72 additions & 0 deletions vr4300/functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,9 @@ cen64_cold static int vr4300_cacheop_dc_wb_invalidate(
bus_write_word(vr4300->bus, bus_address + i * 4,
data[i ^ (WORD_ADDR_XOR >> 2)], ~0);

if (vr4300->regs[VR4300_CP0_REGISTER_LLADDR] == paddr >> 4)
vr4300->regs[VR4300_CP0_REGISTER_LLADDR] = 0;

line->metadata &= ~0x2;
return DCACHE_ACCESS_DELAY;
}
Expand Down Expand Up @@ -543,6 +546,9 @@ cen64_cold static int vr4300_cacheop_dc_hit_wb_invalidate(
bus_write_word(vr4300->bus, bus_address + i * 4,
data[i ^ (WORD_ADDR_XOR >> 2)], ~0);

if (vr4300->regs[VR4300_CP0_REGISTER_LLADDR] == paddr >> 4)
vr4300->regs[VR4300_CP0_REGISTER_LLADDR] = 0;

line->metadata &= ~0x1;
return DCACHE_ACCESS_DELAY;
}
Expand Down Expand Up @@ -570,6 +576,9 @@ cen64_cold static int vr4300_cacheop_dc_hit_wb(
bus_write_word(vr4300->bus, bus_address + i * 4,
data[i ^ (WORD_ADDR_XOR >> 2)], ~0);

if (vr4300->regs[VR4300_CP0_REGISTER_LLADDR] == paddr >> 4)
vr4300->regs[VR4300_CP0_REGISTER_LLADDR] = 0;

// TODO: Technically, it's clean now...
line->metadata &= ~0x2;
return DCACHE_ACCESS_DELAY;
Expand Down Expand Up @@ -1125,6 +1134,69 @@ cen64_hot int VR4300_LOAD_STORE(struct vr4300 *vr4300,
return 0;
}

//
// LL
// SC
//
// TODO/FIXME: Check for unaligned addresses.
//
int VR4300_LOAD_STORE_CONDITIONAL(struct vr4300 *vr4300,
uint32_t iw, uint64_t rs, uint64_t rt) {
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
const uint64_t sel_mask = (int64_t) (int32_t) (iw << 2) >> 32; // 0 for load, -1 for store

const uint64_t address = rs + (int64_t) (int16_t) iw;
const uint64_t dqm = ~0ULL & ~sel_mask; // sign-extend

exdc_latch->request.vaddr = address & ~0x3;
exdc_latch->request.data = dqm | (sel_mask & rt);
exdc_latch->request.wdqm = (uint32_t) sel_mask;
exdc_latch->request.postshift = 0;
exdc_latch->request.access_type = VR4300_ACCESS_WORD;
exdc_latch->request.type = sel_mask ?
VR4300_BUS_REQUEST_WRITE_CONDITIONAL :
VR4300_BUS_REQUEST_READ_CONDITIONAL;
exdc_latch->request.size = 4;

exdc_latch->dest = ~sel_mask & GET_RT(iw);
exdc_latch->result = 0;

if (sel_mask)
exdc_latch->request.sc_reg = GET_RT(iw);

return 0;
}

//
// LLD
// SCD
//
// TODO/FIXME: Check for unaligned addresses.
//
int VR4300_LD_SD_CONDITIONAL(struct vr4300 *vr4300,
uint32_t iw, uint64_t rs, uint64_t rt) {
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
uint64_t sel_mask = (int64_t) (int32_t) (iw << 2) >> 32;

exdc_latch->request.vaddr = rs + (int64_t) (int16_t) iw;
exdc_latch->request.data = ~sel_mask | (sel_mask & rt);
exdc_latch->request.wdqm = sel_mask;
exdc_latch->request.postshift = 0;
exdc_latch->request.access_type = VR4300_ACCESS_DWORD;
exdc_latch->request.type = sel_mask ?
VR4300_BUS_REQUEST_WRITE_CONDITIONAL :
VR4300_BUS_REQUEST_READ_CONDITIONAL;
exdc_latch->request.size = 8;

exdc_latch->dest = ~sel_mask & GET_RT(iw);
exdc_latch->result = 0;

if (sel_mask)
exdc_latch->request.sc_reg = GET_RT(iw);

return 0;
}

//
// LDL
// LDR
Expand Down
8 changes: 4 additions & 4 deletions vr4300/opcodes_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@
#define LDR VR4300_BUILD_OP(LDR, LDL_LDR, INFO3(NEEDRS, NEEDRT, LOAD))
#define LH VR4300_BUILD_OP(LH, LOAD_STORE, INFO2(NEEDRS, LOAD))
#define LHU VR4300_BUILD_OP(LHU, LOAD_STORE, INFO2(NEEDRS, LOAD))
#define LL VR4300_BUILD_OP(LL, INVALID, INFO1(NONE))
#define LLD VR4300_BUILD_OP(LLD, INVALID, INFO1(NONE))
#define LL VR4300_BUILD_OP(LL, LOAD_STORE_CONDITIONAL, INFO2(NEEDRS, LOAD))
#define LLD VR4300_BUILD_OP(LLD, LD_SD_CONDITIONAL, INFO2(NEEDRS, LOAD))
#define LUI VR4300_BUILD_OP(LUI, ADDIU_LUI_SUBIU, INFO1(NONE))
#define LW VR4300_BUILD_OP(LW, LOAD_STORE, INFO2(NEEDRS, LOAD))
#define LWL VR4300_BUILD_OP(LWL, LWL_LWR, INFO3(NEEDRS, NEEDRT, LOAD))
Expand All @@ -102,8 +102,8 @@
#define OR VR4300_BUILD_OP(OR, AND_OR_XOR, INFO2(NEEDRS, NEEDRT))
#define ORI VR4300_BUILD_OP(ORI, ANDI_ORI_XORI, INFO1(NEEDRS))
#define SB VR4300_BUILD_OP(SB, LOAD_STORE, INFO3(NEEDRS, NEEDRT, STORE))
#define SC VR4300_BUILD_OP(SC, INVALID, INFO1(NONE))
#define SCD VR4300_BUILD_OP(SCD, INVALID, INFO1(NONE))
#define SC VR4300_BUILD_OP(SC, LOAD_STORE_CONDITIONAL, INFO3(NEEDRS, NEEDRT, STORE))
#define SCD VR4300_BUILD_OP(SCD, LD_SD_CONDITIONAL, INFO3(NEEDRS, NEEDRT, STORE))
#define SD VR4300_BUILD_OP(SD, LD_SD, INFO3(NEEDRS, NEEDRT, STORE))
#define SDL VR4300_BUILD_OP(SDL, SDL_SDR, INFO3(NEEDRS, NEEDRT, STORE))
#define SDR VR4300_BUILD_OP(SDR, SDL_SDR, INFO3(NEEDRS, NEEDRT, STORE))
Expand Down
96 changes: 96 additions & 0 deletions vr4300/pipeline.c
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,102 @@ static int vr4300_dc_stage(struct vr4300 *vr4300) {
line->metadata |= exdc_latch->request.type;
}

else if (unlikely(exdc_latch->request.type > VR4300_BUS_REQUEST_CACHE_WRITE)) {
// ll/lld/sc/scd
uint64_t dword, rtemp, wtemp, wdqm;
unsigned shiftamt, rshiftamt, lshiftamt;
uint32_t s_paddr;

if (exdc_latch->request.type == VR4300_BUS_REQUEST_READ_CONDITIONAL) {
vr4300->regs[VR4300_REGISTER_LLBIT] = 1;
vr4300->regs[VR4300_CP0_REGISTER_LLADDR] = paddr >> 4;
} else {
if (!vr4300->regs[VR4300_REGISTER_LLBIT] ||
vr4300->regs[VR4300_CP0_REGISTER_LLADDR] != paddr >> 4) {
// Fail the store, set the reg to 0, write nothing to memory
exdc_latch->request.type = VR4300_BUS_REQUEST_NONE;
vr4300->regs[exdc_latch->request.sc_reg] = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Must be written by WB for forwarding:

dcwb_latch->dest = exdc_latch->request.sc_reg;
dcwb_latch->result = 0;

return 0; // fail is not an exception
} else {
// Successful store, set the reg to 1
vr4300->regs[exdc_latch->request.sc_reg] = 1;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a pipelined write? Otherwise, the result may not be correctly forwarded:
https://github.com/n64dev/cen64/blob/master/vr4300/pipeline.c#L173

dcwb_latch->dest = exdc_latch->request.sc_reg;
dcwb_latch->result = 1

}
}

line = vr4300_dcache_probe(&vr4300->dcache, vaddr, paddr);

if (cached) {
bool last_cache_was_store = dcwb_latch->last_op_was_cache_store;
dcwb_latch->last_op_was_cache_store = (exdc_latch->request.type ==
VR4300_BUS_REQUEST_WRITE_CONDITIONAL);

if (!line) {
request->paddr = paddr;
exdc_latch->cached = cached;

if (vr4300->profile_samples) {
uint32_t idx = exdc_latch->common.pc - 0x80000000;
idx &= (8 * 1024 * 1024) - 1;
vr4300->profile_samples[idx + (8 * 1024 * 1024)]++;
}

// Miss: stall for one cycle, then move to the DCM phase.
vr4300->pipeline.cycles_to_stall = 0;
vr4300->regs[PIPELINE_CYCLE_TYPE] = 6;
return 1;
}

// Cached store-to-cached operation will result in a DCB.
if (last_cache_was_store) {
VR4300_DCB(vr4300);
return 1;
}
}

else {
dcwb_latch->last_op_was_cache_store = false;

request->paddr = paddr;
exdc_latch->cached = cached;

if (vr4300->profile_samples) {
uint32_t idx = exdc_latch->common.pc - 0x80000000;
idx &= (8 * 1024 * 1024) - 1;
vr4300->profile_samples[idx + (8 * 1024 * 1024)]++;
}

VR4300_DCM(vr4300);
return 1;
}

s_paddr = paddr << 3;
paddr &= 0x8;

// Pull out the cache line data, mux stuff around
// to produce the output/update the cache line.
memcpy(&dword, line->data + paddr, sizeof(dword));

shiftamt = (s_paddr ^ (WORD_ADDR_XOR << 3)) & request->access_type;
rshiftamt = (8 - request->size) << 3;
lshiftamt = (s_paddr & (0x7 << 3));

wdqm = request->wdqm << shiftamt;
wtemp = (request->data << shiftamt) & wdqm;
rtemp = ((int64_t) (dword << lshiftamt)
>> rshiftamt) & request->data;

dword = (dword & ~wdqm) | wtemp;
dcwb_latch->result |= rtemp << request->postshift;
memcpy(line->data + paddr, &dword, sizeof(dword));

// We need to mark the line dirty if it's write.
// Metadata & 0x2 == dirty, and metadata 0x1 == valid. Map to those.
if (request->type == VR4300_BUS_REQUEST_READ_CONDITIONAL)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this be checking for VR4300_BUS_REQUEST_STORE_CONDITIONAL?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this block may be dedup'd with the original store/load path, replacing this if statement with:
line->metadata |= (exdc_latch->request.type & 0x3);

and carefully picking constant vals for VR4300_BUS_REQUEST_READ_CONDITIONAL and VR4300_BUS_REQUEST_WRITE_CONDITIONAL.

line->metadata |= 1;
else
line->metadata |= 2;
}

// Not a load/store, so execute cache operation.
else {
unsigned delay;
Expand Down
4 changes: 4 additions & 0 deletions vr4300/pipeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ enum vr4300_bus_request_type {
VR4300_BUS_REQUEST_CACHE = 4,
VR4300_BUS_REQUEST_CACHE_IDX = 4,
VR4300_BUS_REQUEST_CACHE_WRITE = 5,
VR4300_BUS_REQUEST_READ_CONDITIONAL,
VR4300_BUS_REQUEST_WRITE_CONDITIONAL,
};

enum vr4300_access_type {
Expand All @@ -45,6 +47,8 @@ struct vr4300_bus_request {
enum vr4300_access_type access_type;
enum vr4300_bus_request_type type;
unsigned size, postshift;

unsigned char sc_reg;
};

struct vr4300_latch {
Expand Down