Skip to content

Commit 15af20a

Browse files
authored
feat: implement direct-mapped cache, cache config modal (#158)
* 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
1 parent 2bcf4cc commit 15af20a

File tree

20 files changed

+1123
-624
lines changed

20 files changed

+1123
-624
lines changed

public/locales/en/common.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,13 @@
7676
"quantity": "Quantity",
7777
"cacheFault": "Cache fault",
7878
"issue": "Issue",
79-
"default": "Default values"
79+
"default": "Default values",
80+
"cacheType": "Cache type",
81+
"cacheLines": "Cache lines",
82+
"cacheBlocks": "Cache blocks",
83+
"noCache": "Without cache",
84+
"directCache": "Direct-mapped",
85+
"randomCache": "Random"
8086
},
8187
"vliwModal": {
8288
"name": "VLIW Configuration",

public/locales/es/common.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,13 @@
7676
"quantity": "Cantidad",
7777
"cacheFault": "Fallo de caché",
7878
"issue": "Emisión",
79-
"default": "Valores por defecto"
79+
"default": "Valores por defecto",
80+
"cacheType": "Tipo de caché",
81+
"cacheLines": "Tamaño de caché",
82+
"cacheBlocks": "Bloques de caché",
83+
"noCache": "Sin caché",
84+
"directCache": "Mapeado directo",
85+
"randomCache": "Aleatoria"
8086
},
8187
"vliwModal": {
8288
"name": "Configuración VLIW",

src/core/Common/Cache.ts

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import type { Memory } from "./Memory";
2+
3+
export enum CacheType {
4+
NO_CACHE = "NO_CACHE",
5+
RANDOM_CACHE = "RANDOM_CACHE",
6+
DIRECT_CACHE = "DIRECT_CACHE",
7+
}
8+
9+
export interface Datum {
10+
value: number;
11+
got: boolean;
12+
}
13+
14+
export abstract class Cache {
15+
public abstract success: boolean;
16+
public abstract getHandler: ProxyHandler<typeof Memory.prototype.getData>;
17+
public abstract setHandler: ProxyHandler<typeof Memory.prototype.setData>;
18+
}
19+
20+
export class RandomCache extends Cache {
21+
public success = true;
22+
public getHandler: ProxyHandler<typeof Memory.prototype.getData>;
23+
public setHandler: ProxyHandler<typeof Memory.prototype.setData> = {};
24+
constructor(public faultChance: number) {
25+
super();
26+
27+
this.getHandler = {
28+
apply: this.applyToGetData.bind(this),
29+
};
30+
}
31+
32+
public applyToGetData(
33+
target: typeof Memory.prototype.getData,
34+
thisArg: any,
35+
args: any[],
36+
) {
37+
this.success = this.faultChance < Math.random();
38+
39+
return Reflect.apply(target, thisArg, args);
40+
}
41+
}
42+
43+
export class DirectCache extends Cache {
44+
public success = true;
45+
public getHandler: ProxyHandler<typeof Memory.prototype.getData>;
46+
public setHandler: ProxyHandler<typeof Memory.prototype.setData>;
47+
private readonly blocks_tags: number[];
48+
private block_size: number;
49+
50+
constructor(
51+
private _blocks: number, // number of blocks
52+
_size: number,
53+
) {
54+
super();
55+
56+
// Calculate the block size
57+
this.block_size = Math.floor(_size / _blocks);
58+
59+
// Initializa the blocks tags to -1
60+
this.blocks_tags = Array(_blocks).fill(-1);
61+
62+
this.getHandler = {
63+
apply: this.applyToGetData.bind(this),
64+
};
65+
66+
this.setHandler = {
67+
apply: this.applyToSetData.bind(this),
68+
};
69+
}
70+
71+
public applyToGetData(
72+
target: typeof Memory.prototype.getData,
73+
thisArg: any,
74+
args: any[],
75+
) {
76+
const address = args[0];
77+
78+
// get the tag of the address
79+
const tag = Math.floor(address / this.block_size);
80+
81+
// check if the tag is in the cache
82+
const faultOccurred = this.blocks_tags[tag % this._blocks] !== tag;
83+
this.success = !faultOccurred;
84+
85+
// set the tag in the cache
86+
this.blocks_tags[tag % this._blocks] = tag;
87+
88+
return Reflect.apply(target, thisArg, args);
89+
}
90+
91+
public applyToSetData(
92+
target: typeof Memory.prototype.getData,
93+
thisArg: any,
94+
args: any[],
95+
) {
96+
const address = args[0];
97+
98+
// get the tag of the address
99+
const tag = Math.floor(address / this.block_size);
100+
101+
// set the tag in the cache
102+
this.blocks_tags[tag % this._blocks] = tag;
103+
104+
return Reflect.apply(target, thisArg, args);
105+
}
106+
}
107+
108+
export function createCache(
109+
cacheType: CacheType,
110+
...args: number[]
111+
): RandomCache | DirectCache | null {
112+
switch (cacheType) {
113+
case CacheType.NO_CACHE:
114+
return null;
115+
case CacheType.RANDOM_CACHE:
116+
return new RandomCache(args[2]);
117+
case CacheType.DIRECT_CACHE:
118+
return new DirectCache(args[0], args[1]);
119+
default:
120+
throw new Error("Invalid cache type");
121+
}
122+
}

src/core/Common/Machine.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
FunctionalUnitType,
66
FunctionalUnitNumbers,
77
} from "./FunctionalUnit";
8+
import { Cache } from "./Cache";
89
import { Memory } from "./Memory";
910

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

3233
protected _functionalUnit: FunctionalUnit[][];
3334
protected _memory: Memory;
35+
protected _cache: Cache;
3436
protected _pc: number;
3537
protected _status: MachineStatus;
3638

@@ -58,6 +60,16 @@ export class Machine {
5860
this._memory = value;
5961
}
6062

63+
public get cache(): Cache {
64+
return this._cache;
65+
}
66+
67+
public set cache(value: Cache) {
68+
this._cache = value;
69+
// Proxy the memory access to the cache
70+
this.resetContent();
71+
}
72+
6173
public get pc(): number {
6274
return this._pc;
6375
}
@@ -136,7 +148,11 @@ export class Machine {
136148
public resetContent() {
137149
this._gpr.setAllContent(0);
138150
this._fpr.setAllContent(0);
139-
this.memory = new Memory(this.memory.size, this.memory.faultChance);
151+
this.memory = new Memory(Machine.MEMORY_SIZE);
152+
if (this.cache) {
153+
this.memory.getData = new Proxy(this.memory.getData, this.cache.getHandler);
154+
this.memory.setData = new Proxy(this.memory.setData, this.cache.setHandler);
155+
}
140156
}
141157

142158
public changeFunctionalUnitLatency(

src/core/Common/Memory.ts

Lines changed: 12 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,23 @@
1-
export interface Datum {
2-
value: number;
3-
got: boolean;
4-
}
5-
61
/**
72
* Returns an Error when target method is called with
83
* a non-positive `address: number` argument.
94
*/
10-
export const forcePositiveAddresses = (address: number): void | Error => {
5+
export const forcePositiveAddresses = (address: number): Error | undefined => {
116
if (address < 0) {
127
return Error("Negative numbers are invalid as addresses");
138
}
149
};
1510

16-
export class Memory implements Iterable<Datum> {
17-
private readonly data: Datum[];
11+
export class Memory implements Iterable<number> {
12+
private readonly data: number[];
1813

19-
constructor(size: number, public faultChance: number = 0.0) {
14+
constructor(size: number) {
2015
// Initialize clean data array with `size` Datum slots.
21-
this.data = Array.from(Array(size).keys()).map((n) => ({
22-
value: 0,
23-
got: true,
24-
}));
16+
this.data = Array(size).fill(0);
2517
}
2618

2719
// Memory iterator
28-
[Symbol.iterator](): IterableIterator<Datum> {
20+
[Symbol.iterator](): IterableIterator<number> {
2921
return this.data.values();
3022
}
3123

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

40-
public getFaultyDatum(address: number): Datum | Error {
41-
let error = forcePositiveAddresses(address);
32+
public getData(address: number): Error | number {
33+
const error = forcePositiveAddresses(address);
4234
if (error) return error;
4335

44-
const datum = this.data[address];
45-
46-
const faultOccurred = this.faultChance > Math.random();
47-
48-
// This will flip 'got' to false if a fault occurred or to true if there was a fault the last time.
49-
// So effectively, we will evite getting the same fault twice in a row.
50-
if (faultOccurred || !datum.got) {
51-
datum.got = !datum.got;
52-
}
53-
54-
return { ...datum };
36+
return this.data[address];
5537
}
5638

57-
public setDatum(address: number, value: number): void | Error {
58-
let error = forcePositiveAddresses(address);
39+
public setData(address: number, value: number): Error | undefined {
40+
const error = forcePositiveAddresses(address);
5941
if (error) return error;
6042

61-
this.data[address] = { ...this.data[address], value };
43+
this.data[address] = value;
6244
}
6345
}

src/core/Superscalar/Superscalar.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ export class Superscalar extends Machine {
492492

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

@@ -501,8 +501,8 @@ export class Superscalar extends Machine {
501501
throw a;
502502
}
503503

504-
resul = a.value;
505-
if (!a.got) {
504+
resul = a;
505+
if (this.cache && !this.cache.success) {
506506
this.functionalUnit[type][num].stall(
507507
this.memoryFailLatency - this.functionalUnit[type][num].latency
508508
);
@@ -582,7 +582,7 @@ export class Superscalar extends Machine {
582582
this._currentCommitedInstrs = new Array<number>();
583583
for (let i = 0; i < this.issue; i++) {
584584
if (this._reorderBuffer.canCommitStoreInstruction()) {
585-
this.memory.setDatum(
585+
this.memory.setData(
586586
this._reorderBuffer.getResultAddress(),
587587
this._reorderBuffer.getResultValue()
588588
);

src/core/VLIW/VLIW.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { VLIWCode } from './VLIWCode';
44
import { FunctionalUnit, FunctionalUnitType, FUNCTIONALUNITTYPESQUANTITY } from '../Common/FunctionalUnit';
55
import { DependencyChecker, Check } from './DependencyChecker';
66
import { VLIWError } from './VLIWError';
7-
import { Datum } from '../Common/Memory';
87
import { VLIWOperation } from './VLIWOperation';
98

109
export class VLIW extends Machine {
@@ -252,41 +251,43 @@ export class VLIW extends Machine {
252251
this._fpr.setContent(operation.getOperand(0), this._fpr.content[operation.getOperand(1)] * this._fpr.content[operation.getOperand(2)], true);
253252
break;
254253
case Opcodes.SW:
255-
this._memory.setDatum(this._gpr.content[operation.getOperand(2)] + operation.getOperand(1), this._gpr.content[operation.getOperand(0)]);
254+
this._memory.setData(this._gpr.content[operation.getOperand(2)] + operation.getOperand(1), this._gpr.content[operation.getOperand(0)]);
256255
break;
257256
case Opcodes.SF:
258-
this._memory.setDatum(this._gpr.content[operation.getOperand(2)] + operation.getOperand(1), this._fpr.content[operation.getOperand(0)]);
257+
this._memory.setData(this._gpr.content[operation.getOperand(2)] + operation.getOperand(1), this._fpr.content[operation.getOperand(0)]);
259258
break;
260-
case Opcodes.LW:
261-
let datumInteger = this._memory.getFaultyDatum(this._gpr.content[operation.getOperand(2)] + operation.getOperand(1));
259+
case Opcodes.LW: {
260+
const datumInteger = this._memory.getData(this._gpr.content[operation.getOperand(2)] + operation.getOperand(1));
262261

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

268-
if (!datumInteger.got) {
267+
if (this._cache && !this._cache.success) {
269268
functionalUnit.stall(this._memoryFailLatency - functionalUnit.latency);
270269
}
271270

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

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

283-
if (!datumFloat.got) {
283+
if (this._cache && !this._cache.success) {
284284
functionalUnit.stall(this._memoryFailLatency - functionalUnit.latency);
285285
}
286286

287-
this._fpr.setContent(operation.getOperand(0), datumFloat.value, true);
287+
this._fpr.setContent(operation.getOperand(0), datumFloat, true);
288288
this._NaTFP[operation.getOperand(0)] = false;
289289
break;
290+
}
290291
default:
291292
break;
292293
}

0 commit comments

Comments
 (0)