Skip to content

Commit cff0b01

Browse files
ezeliolicreinwar
andcommitted
Add hybrid cache / spm support to dcache
Co-authored-by: Christopher Reinwardt <creinwar@iis.ee.ethz.ch>
1 parent 54d629c commit cff0b01

8 files changed

Lines changed: 327 additions & 68 deletions

File tree

Bender.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ sources:
180180
- core/cache_subsystem/wt_cache_subsystem.sv
181181
- core/cache_subsystem/wt_axi_adapter.sv
182182
- core/cache_subsystem/ispm_ctrl.sv
183+
- core/cache_subsystem/dspm_ctrl.sv
183184
- core/cache_subsystem/cva6_icache.sv
184185
- core/cache_subsystem/tag_cmp.sv
185186
- core/cache_subsystem/cache_ctrl.sv

core/cache_subsystem/dspm_ctrl.sv

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
// Copyright 2024 ETH Zurich and University of Bologna.
2+
// Copyright and related rights are licensed under the Solderpad Hardware
3+
// License, Version 0.51 (the "License"); you may not use this file except in
4+
// compliance with the License. You may obtain a copy of the License at
5+
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
6+
// or agreed to in writing, software, hardware and materials distributed under
7+
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
8+
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
9+
// specific language governing permissions and limitations under the License.
10+
//
11+
// Author: Christopher Reinwardt <creinwar@student.ethz.ch>
12+
// --------------
13+
// D-Cache SPM controller
14+
// --------------
15+
//
16+
// Description: Arbitrates access to data cache memories in SPM mode
17+
// Requests from lower index input ports are prioritized
18+
//
19+
20+
`include "common_cells/registers.svh"
21+
22+
module dspm_ctrl
23+
import std_cache_pkg::*;
24+
import ariane_pkg::*;
25+
#(
26+
parameter type dcache_req_i_t = logic,
27+
parameter type dcache_req_o_t = logic,
28+
parameter int unsigned NR_PORTS = 3,
29+
parameter int unsigned NR_WAYS = 4,
30+
parameter int unsigned LINE_WIDTH = 128,
31+
parameter int unsigned ADDR_WIDTH = 64,
32+
parameter int unsigned MEMORY_WIDTH = 172,
33+
parameter int unsigned IDX_WIDTH = 12, // Cache index + byte offset
34+
parameter int unsigned NR_WAIT_STAGES = 1,
35+
// Derived parameters, DO NOT OVERRIDE
36+
localparam int unsigned BE_WIDTH = (MEMORY_WIDTH + 7) / 8
37+
) (
38+
input logic clk_i,
39+
input logic rst_ni,
40+
41+
input logic [NR_WAYS-1:0] active_ways_i,
42+
43+
// Request ports
44+
input dcache_req_i_t [NR_PORTS-1:0] spm_req_ports_i,
45+
output dcache_req_o_t [NR_PORTS-1:0] spm_req_ports_o,
46+
47+
output logic [ NR_WAYS-1:0] req_o,
48+
output logic [ ADDR_WIDTH-1:0] addr_o,
49+
output logic [MEMORY_WIDTH-1:0] wdata_o,
50+
output logic we_o,
51+
output logic [ BE_WIDTH-1:0] be_o,
52+
input logic [ NR_WAYS-1:0][MEMORY_WIDTH-1:0] rdata_i
53+
);
54+
55+
localparam WAY_INDEX_BITS = $clog2(NR_WAYS);
56+
logic [WAY_INDEX_BITS-1:0] way_idx, way_idx_d, way_idx_q;
57+
58+
// One hot encoded
59+
logic [$clog2(NR_PORTS)-1:0] portsel, portsel_d, portsel_q;
60+
61+
// SRAM latency counter
62+
logic [$clog2(NR_WAIT_STAGES)-1:0] wait_stage_d, wait_stage_q;
63+
64+
// Helper variable to assemble the full cache-line from parts
65+
logic [LINE_WIDTH-1:0] write_line;
66+
67+
// Word offset within the cacheline
68+
logic [$clog2(LINE_WIDTH/8)-$clog2(riscv::XLEN/8)-1:0] cl_offset, cl_offset_d, cl_offset_q;
69+
70+
// Port selection logic
71+
always_comb begin
72+
portsel = '{default: 0};
73+
74+
for (int unsigned i = 0; i < NR_PORTS; i++) begin
75+
// This data request is only coming from the actual requester
76+
// if it is a write
77+
// Otherwise this is coming from the request splitter
78+
// => Only grant if it is a write
79+
if (spm_req_ports_i[i].data_req) begin
80+
portsel = i;
81+
break;
82+
end
83+
end
84+
end
85+
86+
// Static assignments
87+
// This saves which cache way this address targets
88+
assign way_idx = spm_req_ports_i[portsel].address_tag[0+:WAY_INDEX_BITS];
89+
90+
// This saves at which offset within a cacheline we are
91+
assign cl_offset = spm_req_ports_i[portsel].address_index[$clog2(
92+
LINE_WIDTH/8
93+
)-1:$clog2(
94+
riscv::XLEN/8
95+
)];
96+
97+
assign addr_o = spm_req_ports_i[portsel].address_index;
98+
// This zeros the tag and other status bits,
99+
// while writing the payload to the SRAM
100+
assign wdata_o = {{(MEMORY_WIDTH - LINE_WIDTH) {1'b0}}, write_line};
101+
assign we_o = spm_req_ports_i[portsel].data_we;
102+
103+
always_comb begin
104+
cl_offset_d = cl_offset_q;
105+
portsel_d = portsel_q;
106+
wait_stage_d = wait_stage_q;
107+
way_idx_d = way_idx_q;
108+
109+
spm_req_ports_o = '{default: 0};
110+
111+
write_line = '{default: 0};
112+
// We assemble the write data unconditionally as the
113+
// write is controlled by the write enable
114+
write_line[(cl_offset*riscv::XLEN)+:riscv::XLEN] = spm_req_ports_i[portsel].data_wdata;
115+
116+
req_o = '{default: 0};
117+
118+
// By default we'll always write the tag (so that it's zeroed)
119+
be_o = '{default: 0};
120+
be_o[BE_WIDTH-1:(LINE_WIDTH/8)] = '{default: 1'b1};
121+
// Only enable the part of the cacheline that we actually want to update
122+
be_o[(cl_offset*(riscv::XLEN/8))+:(riscv::XLEN/8)] = spm_req_ports_i[portsel].data_be;
123+
124+
// Decrease the wait counter if it's not already 0
125+
if (wait_stage_q) wait_stage_d = wait_stage_q - ($clog2(NR_WAIT_STAGES) + 1)'(1);
126+
127+
// Accept a new request if one is pending and we're not waiting for
128+
// the SRAM anymore
129+
if (spm_req_ports_i[portsel].data_req && wait_stage_q == '0) begin
130+
portsel_d = portsel;
131+
way_idx_d = way_idx;
132+
// Record the offset
133+
cl_offset_d = cl_offset;
134+
135+
// Reset the counter on every new request
136+
wait_stage_d = NR_WAIT_STAGES;
137+
138+
// Are we allowed to use this memory?
139+
if (active_ways_i[way_idx]) begin
140+
req_o[way_idx] = 1'b1;
141+
142+
// Otherwise just respond with badcable
143+
end else begin
144+
spm_req_ports_o[portsel].data_rdata = 64'hCA11AB1E_BADCAB1E;
145+
spm_req_ports_o[portsel].data_gnt = spm_req_ports_i[portsel].data_we;
146+
spm_req_ports_o[portsel].data_rvalid = ~spm_req_ports_i[portsel].data_we;
147+
148+
// We don't need to wait for the memory here, as we did not access any
149+
wait_stage_d = '0;
150+
end
151+
end
152+
153+
// Later acknowledge => Use the registered version of the select signals
154+
if (wait_stage_q == 32'b1) begin
155+
spm_req_ports_o[portsel_q].data_gnt = spm_req_ports_i[portsel_q].data_we;
156+
spm_req_ports_o[portsel_q].data_rvalid = ~spm_req_ports_i[portsel_q].data_we;
157+
spm_req_ports_o[portsel_q].data_rdata = rdata_i[way_idx_q][(cl_offset_q * riscv::XLEN) +: riscv::XLEN];
158+
159+
// Same cycle acknowledge => Use the unregistered version
160+
end else if (NR_WAIT_STAGES == 0) begin
161+
162+
spm_req_ports_o[portsel].data_gnt = spm_req_ports_i[portsel].data_we;
163+
spm_req_ports_o[portsel].data_rvalid = ~spm_req_ports_i[portsel].data_we;
164+
spm_req_ports_o[portsel].data_rdata = rdata_i[way_idx][(cl_offset*riscv::XLEN)+:riscv::XLEN];
165+
end
166+
end
167+
168+
`FF(cl_offset_q, cl_offset_d, '0, clk_i, rst_ni)
169+
`FF(wait_stage_q, wait_stage_d, '0, clk_i, rst_ni)
170+
`FF(way_idx_q, way_idx_d, '0, clk_i, rst_ni)
171+
`FF(portsel_q, portsel_d, '0, clk_i, rst_ni)
172+
173+
endmodule

core/cache_subsystem/miss_handler.sv

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ module miss_handler
3535
output logic miss_o,
3636
input logic busy_i, // dcache is busy with something
3737
input logic init_ni, // do not init after reset
38+
input logic [CVA6Cfg.DCACHE_SET_ASSOC-1:0] spm_ways_i, // the mask of cache ways currently owned by the SPM
3839
// Bypass or miss
3940
input logic [NR_PORTS-1:0][$bits(miss_req_t)-1:0] miss_req_i,
4041
// Bypass handling
@@ -177,8 +178,8 @@ module miss_handler
177178
automatic logic [CVA6Cfg.DCACHE_SET_ASSOC-1:0] evict_way, valid_way;
178179

179180
for (int unsigned i = 0; i < CVA6Cfg.DCACHE_SET_ASSOC; i++) begin
180-
evict_way[i] = data_i[i].valid & (|data_i[i].dirty);
181-
valid_way[i] = data_i[i].valid;
181+
evict_way[i] = data_i[i].valid & (|data_i[i].dirty) & ~spm_ways_i[i];
182+
valid_way[i] = data_i[i].valid | spm_ways_i[i];
182183
end
183184
// ----------------------
184185
// Default Assignments
@@ -681,15 +682,37 @@ module miss_handler
681682
// -----------------
682683
// Replacement LFSR
683684
// -----------------
685+
686+
logic [CVA6Cfg.DCACHE_SET_ASSOC-1:0] lfsr_oh_raw, cache_ways_oh;
687+
logic [$clog2(CVA6Cfg.DCACHE_SET_ASSOC)-1:0] lfsr_bin_raw, cache_ways_bin;
688+
684689
lfsr_8bit #(
685690
.WIDTH(CVA6Cfg.DCACHE_SET_ASSOC)
686691
) i_lfsr (
687692
.en_i (lfsr_enable),
688-
.refill_way_oh (lfsr_oh),
689-
.refill_way_bin(lfsr_bin),
693+
.refill_way_oh (lfsr_oh_raw),
694+
.refill_way_bin(lfsr_bin_raw),
690695
.*
691696
);
692697

698+
// Calculate the first cache owned way
699+
assign cache_ways_oh = (spm_ways_i + 1'b1) & ~spm_ways_i;
700+
701+
// And the binary representation of it
702+
always_comb begin
703+
cache_ways_bin = 0;
704+
for (int unsigned i = 0; i < CVA6Cfg.DCACHE_SET_ASSOC; i++) begin
705+
if (cache_ways_oh[i]) begin
706+
cache_ways_bin = i;
707+
end
708+
end
709+
end
710+
711+
// If the LFSR chose a cache owned way we can use that one
712+
// otherwise use the first cache owned way
713+
assign lfsr_oh = (lfsr_oh_raw & ~spm_ways_i) ? lfsr_oh_raw : cache_ways_oh;
714+
assign lfsr_bin = (lfsr_oh_raw & ~spm_ways_i) ? lfsr_bin_raw : cache_ways_bin;
715+
693716
// -----------------
694717
// Struct Split
695718
// -----------------

core/cache_subsystem/std_cache_subsystem.sv

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ module std_cache_subsystem
6161
input logic dcache_flush_i, // high until acknowledged
6262
output logic dcache_flush_ack_o, // send a single cycle acknowledge signal when the cache is flushed
6363
output logic dcache_miss_o, // we missed on a ld/st
64+
input [CVA6Cfg.DCACHE_SET_ASSOC-1:0] dcache_spm_ways_i, // dcache ways configured as SPM
6465
output logic wbuffer_empty_o, // statically set to 1, as there is no wbuffer in this cache system
6566
// Request ports
6667
input dcache_req_i_t [NumPorts-1:0] dcache_req_ports_i, // to/from LSU
@@ -142,6 +143,7 @@ module std_cache_subsystem
142143
.busy_o (dcache_busy),
143144
.stall_i (stall_i),
144145
.init_ni (init_ni),
146+
.dcache_spm_ways_i(dcache_spm_ways_i),
145147
.axi_bypass_o(axi_req_bypass),
146148
.axi_bypass_i(axi_resp_bypass),
147149
.axi_data_o (axi_req_data),

0 commit comments

Comments
 (0)