Skip to content

Commit

Permalink
feat: implement direct-mapped cache, cache config modal (#158)
Browse files Browse the repository at this point in the history
* refactor(core): new cache aproach

* feat(core): implement direct mapped cache

* add cache configs to SuperscalarConfigModal and fix NoCache

* integrate cache changes into the VLIW

* fix(interface): memory not showing

* fix(tests): replace memory accesses to cache

* tests(core): add unit cache tests and fix DirectCache (TLDR: tests are important)

* remove unecesarry import

* refacfor: apply PR suggestions

* refact(core): first working implementation of cache using proxies

* refact(core): the cache proxies the memory access funtions

* fix(cache): remove a strange import inserted by copilot
  • Loading branch information
endes0 authored Apr 28, 2024
1 parent 2bcf4cc commit 15af20a
Show file tree
Hide file tree
Showing 20 changed files with 1,123 additions and 624 deletions.
8 changes: 7 additions & 1 deletion public/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,13 @@
"quantity": "Quantity",
"cacheFault": "Cache fault",
"issue": "Issue",
"default": "Default values"
"default": "Default values",
"cacheType": "Cache type",
"cacheLines": "Cache lines",
"cacheBlocks": "Cache blocks",
"noCache": "Without cache",
"directCache": "Direct-mapped",
"randomCache": "Random"
},
"vliwModal": {
"name": "VLIW Configuration",
Expand Down
8 changes: 7 additions & 1 deletion public/locales/es/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,13 @@
"quantity": "Cantidad",
"cacheFault": "Fallo de caché",
"issue": "Emisión",
"default": "Valores por defecto"
"default": "Valores por defecto",
"cacheType": "Tipo de caché",
"cacheLines": "Tamaño de caché",
"cacheBlocks": "Bloques de caché",
"noCache": "Sin caché",
"directCache": "Mapeado directo",
"randomCache": "Aleatoria"
},
"vliwModal": {
"name": "Configuración VLIW",
Expand Down
122 changes: 122 additions & 0 deletions src/core/Common/Cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import type { Memory } from "./Memory";

export enum CacheType {
NO_CACHE = "NO_CACHE",
RANDOM_CACHE = "RANDOM_CACHE",
DIRECT_CACHE = "DIRECT_CACHE",
}

export interface Datum {
value: number;
got: boolean;
}

export abstract class Cache {
public abstract success: boolean;
public abstract getHandler: ProxyHandler<typeof Memory.prototype.getData>;
public abstract setHandler: ProxyHandler<typeof Memory.prototype.setData>;
}

export class RandomCache extends Cache {
public success = true;
public getHandler: ProxyHandler<typeof Memory.prototype.getData>;
public setHandler: ProxyHandler<typeof Memory.prototype.setData> = {};
constructor(public faultChance: number) {
super();

this.getHandler = {
apply: this.applyToGetData.bind(this),
};
}

public applyToGetData(
target: typeof Memory.prototype.getData,
thisArg: any,
args: any[],
) {
this.success = this.faultChance < Math.random();

return Reflect.apply(target, thisArg, args);
}
}

export class DirectCache extends Cache {
public success = true;
public getHandler: ProxyHandler<typeof Memory.prototype.getData>;
public setHandler: ProxyHandler<typeof Memory.prototype.setData>;
private readonly blocks_tags: number[];
private block_size: number;

constructor(
private _blocks: number, // number of blocks
_size: number,
) {
super();

// Calculate the block size
this.block_size = Math.floor(_size / _blocks);

// Initializa the blocks tags to -1
this.blocks_tags = Array(_blocks).fill(-1);

this.getHandler = {
apply: this.applyToGetData.bind(this),
};

this.setHandler = {
apply: this.applyToSetData.bind(this),
};
}

public applyToGetData(
target: typeof Memory.prototype.getData,
thisArg: any,
args: any[],
) {
const address = args[0];

// get the tag of the address
const tag = Math.floor(address / this.block_size);

// check if the tag is in the cache
const faultOccurred = this.blocks_tags[tag % this._blocks] !== tag;
this.success = !faultOccurred;

// set the tag in the cache
this.blocks_tags[tag % this._blocks] = tag;

return Reflect.apply(target, thisArg, args);
}

public applyToSetData(
target: typeof Memory.prototype.getData,
thisArg: any,
args: any[],
) {
const address = args[0];

// get the tag of the address
const tag = Math.floor(address / this.block_size);

// set the tag in the cache
this.blocks_tags[tag % this._blocks] = tag;

return Reflect.apply(target, thisArg, args);
}
}

export function createCache(
cacheType: CacheType,
...args: number[]
): RandomCache | DirectCache | null {
switch (cacheType) {
case CacheType.NO_CACHE:
return null;
case CacheType.RANDOM_CACHE:
return new RandomCache(args[2]);
case CacheType.DIRECT_CACHE:
return new DirectCache(args[0], args[1]);
default:
throw new Error("Invalid cache type");
}
}
18 changes: 17 additions & 1 deletion src/core/Common/Machine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
FunctionalUnitType,
FunctionalUnitNumbers,
} from "./FunctionalUnit";
import { Cache } from "./Cache";
import { Memory } from "./Memory";

const MACHINE_REGISTER_SIZE = 64;
Expand All @@ -31,6 +32,7 @@ export class Machine {

protected _functionalUnit: FunctionalUnit[][];
protected _memory: Memory;
protected _cache: Cache;
protected _pc: number;
protected _status: MachineStatus;

Expand Down Expand Up @@ -58,6 +60,16 @@ export class Machine {
this._memory = value;
}

public get cache(): Cache {
return this._cache;
}

public set cache(value: Cache) {
this._cache = value;
// Proxy the memory access to the cache
this.resetContent();
}

public get pc(): number {
return this._pc;
}
Expand Down Expand Up @@ -136,7 +148,11 @@ export class Machine {
public resetContent() {
this._gpr.setAllContent(0);
this._fpr.setAllContent(0);
this.memory = new Memory(this.memory.size, this.memory.faultChance);
this.memory = new Memory(Machine.MEMORY_SIZE);
if (this.cache) {
this.memory.getData = new Proxy(this.memory.getData, this.cache.getHandler);
this.memory.setData = new Proxy(this.memory.setData, this.cache.setHandler);
}
}

public changeFunctionalUnitLatency(
Expand Down
42 changes: 12 additions & 30 deletions src/core/Common/Memory.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,23 @@
export interface Datum {
value: number;
got: boolean;
}

/**
* Returns an Error when target method is called with
* a non-positive `address: number` argument.
*/
export const forcePositiveAddresses = (address: number): void | Error => {
export const forcePositiveAddresses = (address: number): Error | undefined => {
if (address < 0) {
return Error("Negative numbers are invalid as addresses");
}
};

export class Memory implements Iterable<Datum> {
private readonly data: Datum[];
export class Memory implements Iterable<number> {
private readonly data: number[];

constructor(size: number, public faultChance: number = 0.0) {
constructor(size: number) {
// Initialize clean data array with `size` Datum slots.
this.data = Array.from(Array(size).keys()).map((n) => ({
value: 0,
got: true,
}));
this.data = Array(size).fill(0);
}

// Memory iterator
[Symbol.iterator](): IterableIterator<Datum> {
[Symbol.iterator](): IterableIterator<number> {
return this.data.values();
}

Expand All @@ -37,27 +29,17 @@ export class Memory implements Iterable<Datum> {
return this.data.length;
}

public getFaultyDatum(address: number): Datum | Error {
let error = forcePositiveAddresses(address);
public getData(address: number): Error | number {
const error = forcePositiveAddresses(address);
if (error) return error;

const datum = this.data[address];

const faultOccurred = this.faultChance > Math.random();

// This will flip 'got' to false if a fault occurred or to true if there was a fault the last time.
// So effectively, we will evite getting the same fault twice in a row.
if (faultOccurred || !datum.got) {
datum.got = !datum.got;
}

return { ...datum };
return this.data[address];
}

public setDatum(address: number, value: number): void | Error {
let error = forcePositiveAddresses(address);
public setData(address: number, value: number): Error | undefined {
const error = forcePositiveAddresses(address);
if (error) return error;

this.data[address] = { ...this.data[address], value };
this.data[address] = value;
}
}
8 changes: 4 additions & 4 deletions src/core/Superscalar/Superscalar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ export class Superscalar extends Machine {

// load and stores are a special cases, because they need to access the memory
if (inst.isLoadInstruction()) {
let a = this.memory.getFaultyDatum(
const a = this.memory.getData(
this._reserveStations[type].getAddressOperand(instUid)
);

Expand All @@ -501,8 +501,8 @@ export class Superscalar extends Machine {
throw a;
}

resul = a.value;
if (!a.got) {
resul = a;
if (this.cache && !this.cache.success) {
this.functionalUnit[type][num].stall(
this.memoryFailLatency - this.functionalUnit[type][num].latency
);
Expand Down Expand Up @@ -582,7 +582,7 @@ export class Superscalar extends Machine {
this._currentCommitedInstrs = new Array<number>();
for (let i = 0; i < this.issue; i++) {
if (this._reorderBuffer.canCommitStoreInstruction()) {
this.memory.setDatum(
this.memory.setData(
this._reorderBuffer.getResultAddress(),
this._reorderBuffer.getResultValue()
);
Expand Down
23 changes: 12 additions & 11 deletions src/core/VLIW/VLIW.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { VLIWCode } from './VLIWCode';
import { FunctionalUnit, FunctionalUnitType, FUNCTIONALUNITTYPESQUANTITY } from '../Common/FunctionalUnit';
import { DependencyChecker, Check } from './DependencyChecker';
import { VLIWError } from './VLIWError';
import { Datum } from '../Common/Memory';
import { VLIWOperation } from './VLIWOperation';

export class VLIW extends Machine {
Expand Down Expand Up @@ -252,41 +251,43 @@ export class VLIW extends Machine {
this._fpr.setContent(operation.getOperand(0), this._fpr.content[operation.getOperand(1)] * this._fpr.content[operation.getOperand(2)], true);
break;
case Opcodes.SW:
this._memory.setDatum(this._gpr.content[operation.getOperand(2)] + operation.getOperand(1), this._gpr.content[operation.getOperand(0)]);
this._memory.setData(this._gpr.content[operation.getOperand(2)] + operation.getOperand(1), this._gpr.content[operation.getOperand(0)]);
break;
case Opcodes.SF:
this._memory.setDatum(this._gpr.content[operation.getOperand(2)] + operation.getOperand(1), this._fpr.content[operation.getOperand(0)]);
this._memory.setData(this._gpr.content[operation.getOperand(2)] + operation.getOperand(1), this._fpr.content[operation.getOperand(0)]);
break;
case Opcodes.LW:
let datumInteger = this._memory.getFaultyDatum(this._gpr.content[operation.getOperand(2)] + operation.getOperand(1));
case Opcodes.LW: {
const datumInteger = this._memory.getData(this._gpr.content[operation.getOperand(2)] + operation.getOperand(1));

//hack: as we dont have a well made error handling, intercept the error and just throw it
if (datumInteger instanceof Error) {
throw datumInteger;
}

if (!datumInteger.got) {
if (this._cache && !this._cache.success) {
functionalUnit.stall(this._memoryFailLatency - functionalUnit.latency);
}

this._gpr.setContent(operation.getOperand(0), datumInteger.value, true);
this._gpr.setContent(operation.getOperand(0), datumInteger, true);
this._NaTGP[operation.getOperand(0)] = false;
break;
case Opcodes.LF:
let datumFloat = this._memory.getFaultyDatum(this._gpr.content[operation.getOperand(2)] + operation.getOperand(1));
}
case Opcodes.LF: {
const datumFloat = this._memory.getData(this._gpr.content[operation.getOperand(2)] + operation.getOperand(1));

//hack: as we dont have a well made error handling, intercept the error and just throw it
if (datumFloat instanceof Error) {
throw datumFloat;
}

if (!datumFloat.got) {
if (this._cache && !this._cache.success) {
functionalUnit.stall(this._memoryFailLatency - functionalUnit.latency);
}

this._fpr.setContent(operation.getOperand(0), datumFloat.value, true);
this._fpr.setContent(operation.getOperand(0), datumFloat, true);
this._NaTFP[operation.getOperand(0)] = false;
break;
}
default:
break;
}
Expand Down
Loading

0 comments on commit 15af20a

Please sign in to comment.