1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
|
`include "cache/defs.sv"
module cache_routing
(
input logic clk,
rst_n,
input ptr core_address,
input logic core_read,
core_write,
input line core_writedata_line,
input line_be core_byteenable_line,
output logic core_waitrequest,
output line core_readdata_line,
output addr_tag core_tag,
output addr_index core_index,
output addr_offset core_offset,
input line data_rd,
input logic cache_core_waitrequest,
output logic cache_core_read,
cache_core_write,
input word cache_mem_address,
input logic cache_mem_read,
cache_mem_write,
input line cache_mem_writedata,
output logic cache_mem_waitrequest,
input logic mem_waitrequest,
input line mem_readdata,
output word mem_address,
output logic mem_read,
mem_write,
output line mem_writedata,
output line_be mem_byteenable
);
/* Módulo para enrutar las operaciones a cache o memoria
* Esto porque hay escrituras que definitivamente no pueden quedar en cache
* como el caso de periféricos, para los cuales si se guarda "su valor" en
* cache y no en memoria se harían lecturas incorrectas
*/
word core_address_line;
logic cached, cache_mem, transition;
addr_io_region io;
enum int unsigned
{
IDLE,
CACHE,
BYPASS
} state;
//Arbitrar el bus del lado de la cache
/* Se sabe si el address es cache o no evaluando los bits de IO.
* Esto es posible porque se cumple lo siguiente:
* - La memoria tiene un tamaño que es una potencia de 2
* - Sus direcciones inician en 0
* Entonces si los bits de IO son distintos de 0, se sabe que no es
* una dirección cached
*/
assign cached = io == 3'b000;
// Se afirma si cache quiere hacer un read o write de memoria
assign cache_mem = cache_mem_read || cache_mem_write;
// Acá se divide el core_address para analizarse por separado
assign {io, core_tag, core_index, core_offset} = core_address;
assign core_address_line = {io, core_tag, core_index, 4'b0000};
// Si está cached se asigna a lectura de cache, sino a lectura de memoria
assign core_readdata_line = cached ? data_rd : mem_readdata;
// Se afirma si el core quiere leer/escribir a cache y efectivamente es una
// dirección de cache
assign cache_core_read = core_read && cached;
assign cache_core_write = core_write && cached;
// Máquina de estados:
// IDLE/CACHE/BYPASS
// Bypass: el request evita pasar por caché, para que no quede escrito el
// el dato. Esto sirve para periféricos, por ejemplo.
// Cache: el request sí pasa por caché, esto sucede para todo lo que va
// para RAM.
always_comb begin
transition = 0;
core_waitrequest = cache_core_waitrequest;
// Desde el punto de vista de cache, mem le hace waitreq a cache
cache_mem_waitrequest = 1;
unique case (state)
IDLE:
/* Transition se afirma si cache quiere hacer un read o write de
* memoria, o si el address no es cache y el core quiere leer
* o escribir a cache
*/
transition = cache_mem || (!cached && (core_read || core_write));
CACHE:
// Cache le hace waitreq a memoria
cache_mem_waitrequest = mem_waitrequest;
BYPASS:
// Se le hace waitreq al core si la memoria también lo hace
core_waitrequest = mem_waitrequest;
endcase
end
always_ff @(posedge clk or negedge rst_n)
if (!rst_n) begin
state <= IDLE;
mem_read <= 0;
mem_write <= 0;
end else unique case (state)
IDLE:
if (transition) begin
// Si cache quiere hacer una operación con memoria, se pasa
// a CACHE, sino hay que hacer BYPASS. Si la operación
// no es directo a memoria, se hace bypasss porque esto
// implica que el dato no tiene que quedar cacheado. (Talvez
// es algo de un periférico, etc.)
state <= cache_mem ? CACHE : BYPASS;
mem_read <= cache_mem ? cache_mem_read : core_read;
mem_write <= cache_mem ? cache_mem_write : core_write;
end
CACHE, BYPASS:
if (!mem_waitrequest) begin
state <= IDLE;
mem_read <= 0;
mem_write <= 0;
end
endcase
always_ff @(posedge clk)
if (transition) begin
// Si cache no quiere hacer una operación con memoria, se asignan
// las señales del core
mem_address <= cache_mem ? cache_mem_address : core_address_line;
mem_writedata <= cache_mem ? cache_mem_writedata : core_writedata_line;
mem_byteenable <= cache_mem ? 16'hffff : core_byteenable_line;
end
endmodule
|