2 sys
.path
.append("../src/ariane")
4 from nmigen
import Array
, Cat
, Memory
, Module
, Signal
5 from nmigen
.compat
.genlib
import fsm
6 from nmigen
.cli
import main
7 from nmigen
.cli
import verilog
, rtlil
9 from AddressEncoder
import AddressEncoder
11 # TODO: use a LFSR that advances continuously and picking the bottom
12 # few bits from it to select which cache line to replace, instead of PLRU
13 # http://bugs.libre-riscv.org/show_bug.cgi?id=71
16 SA_NA
= "00" # no action (none)
21 def __init__(self
, memory_width
, set_count
, active
,
22 tag_start
, tag_end
, tag_size
):
23 #self.memory_width = memory_width
24 #self.set_count = set_count
25 self
.tag_start
= tag_start
26 self
.tag_end
= tag_end
28 self
.mem
= Memory(memory_width
, set_count
)
29 self
.r
= self
.mem
.read_port()
30 self
.w
= self
.mem
.write_port()
33 self
.cset
= Signal(max=set_count
) # The set to be checked
34 self
.tag
= Signal(tag_size
) # The tag to find
37 self
.active_bit
= Signal()
38 self
.tag_valid
= Signal()
41 def elaborate(self
, platform
):
43 m
.submodules
.mem
= self
.mem
44 m
.submodules
.r
= self
.r
45 m
.submodules
.w
= self
.w
48 m
.d
.comb
+= read_port
.addr
.eq(self
.cset
)
49 # Pull out active bit from data
51 m
.d
.comb
+= self
.active_bit
.eq(data
[self
.active
])
52 # Validate given tag vs stored tag
53 tag
= data
[self
.tag_start
:self
.tag_end
]
54 m
.d
.comb
+= self
.tag_valid
.eq(self
.tag
== tag
)
55 # An entry is only valid if the tags match AND
56 # is marked as a valid entry
57 m
.d
.comb
+= self
.valid
.eq(self
.tag_valid
& self
.active_bit
)
62 class SetAssociativeCache():
63 """ Set Associative Cache Memory
65 The purpose of this module is to generate a memory cache given the
66 constraints passed in. This will create a n-way set associative cache.
67 It is expected for the SV TLB that the VMA will provide the set number
68 while the ASID provides the tag (still to be decided).
71 def __init__(self
, tag_size
, data_size
, set_count
, way_count
):
73 * tag_size (bits): The bit count of the tag
74 * data_size (bits): The bit count of the data to be stored
75 * set_count (number): The number of sets/entries in the cache
76 * way_count (number): The number of slots a data can be stored
81 self
.data_start
= active
+ 1
82 self
.data_end
= self
.data_start
+ data_size
83 self
.tag_start
= self
.data_end
84 self
.tag_end
= self
.tag_start
+ tag_size
85 input_size
= tag_size
+ data_size
# Size of the input data
86 memory_width
= input_size
+ 1 # The width of the cache memory
87 self
.mem_array
= Array() # memory array
89 for i
in range(way_count
):
90 ms
= MemorySet(memory_width
, set_count
, active
,
91 tag_size
, self
.tag_start
, self
.tag_end
)
92 self
.mem_array
.append(ms
)
94 self
.way_count
= way_count
# The number of slots in one set
95 self
.tag_size
= tag_size
# The bit count of the tag
96 self
.data_size
= data_size
# The bit count of the data to be stored
99 self
.encoder
= AddressEncoder(way_count
)
101 self
.plru
= PLRU(way_count
) # Single block to handle plru calculations
102 self
.plru_array
= Array() # PLRU data on each set
103 for i
in range(set_count
):
104 self
.plru_array
.append(Signal(self
.plru
.TLBSZ
, name
="plru%d" % i
))
107 self
.enable
= Signal(1) # Whether the cache is enabled
108 self
.command
= Signal(2) # 00=None, 01=Read, 10=Write (see SA_XX)
109 self
.cset
= Signal(max=set_count
) # The set to be checked
110 self
.tag
= Signal(tag_size
) # The tag to find
111 self
.data_i
= Signal(data_size
) # The input data
114 self
.ready
= Signal(1) # 0 => Processing 1 => Ready for commands
115 self
.hit
= Signal(1) # Tag matched one way in the given set
116 self
.multiple_hit
= Signal(1) # Tag matched many ways in the given set
117 self
.data_o
= Signal(data_size
) # The data linked to the matched tag
119 def check_tags(self
, m
):
120 """ Validate the tags in the selected set. If one and only one
121 tag matches set its state to zero and increment all others
122 by one. We only advance to next state if a single hit is found.
124 # Vector to store way valid results
125 # A zero denotes a way is invalid
127 # Loop through memory to prep read/write ports and set valid_vector
129 for i
in range(self
.way_count
):
130 valid_vector
.append(self
.mem_array
[i
].valid
)
132 # Pass encoder the valid vector
133 m
.d
.comb
+= self
.encoder
.i
.eq(Cat(*valid_vector
))
134 # Only one entry should be marked
135 # This is due to already verifying the tags
136 # matched and the valid bit is high
138 m
.next
= "FINISHED_READ"
139 # Pull out data from the read port
140 read_port
= self
.mem_array
[self
.encoder
.o
].r
141 data
= read_port
.data
[self
.data_start
:self
.data_end
]
146 # Oh no! Seal the gates! Multiple tags matched?!? kasd;ljkafdsj;k
147 with m
.Elif(self
.multiple_hit
):
148 # XXX TODO, m.next = "FINISHED_READ" ? otherwise stuck
152 # No tag matches means no data
154 # XXX TODO, m.next = "FINISHED_READ" ? otherwise stuck
159 def access_plru(self
, m
):
160 """ An entry was accessed and the plru tree must now be updated
162 # Pull out the set's entry being edited
163 plru_entry
= self
.plru_array
[self
.cset
]
165 # Set the plru data to the current state
166 self
.plru
.plru_tree
.eq(plru_entry
),
167 # Set what entry was just hit
168 self
.plru
.lu_hit
.eq(self
.encoder
.o
),
169 # Set that the cache was accessed
170 self
.plru
.lu_access_i
.eq(1)
174 """ Go through the read process of the cache.
175 This takes two cycles to complete. First it checks for a valid tag
176 and secondly it updates the LRU values.
178 with m
.FSM() as fsm_read
:
179 with m
.State("READY"):
180 m
.d
.comb
+= self
.ready
.eq(0)
181 # check_tags will set the state if the conditions are met
183 with m
.State("FINISHED_READ"):
185 m
.d
.comb
+= self
.ready
.eq(1)
186 m
.d
.sync
+= self
.plru_array
[self
.cset
].eq(self
.plru
.plru_tree_o
)
188 def write_entry(self
, m
):
189 lru_entry
= self
.plru
.replace_en_o
190 plru_entry
= self
.plru_array
[self
.cset
]
192 self
.plru
.plru_tree
.eq(plru_entry
),
193 self
.encoder
.i
.eq(lru_entry
)
196 with m
.If(self
.encoder
.single_match
):
197 write_port
= self
.mem_array
[self
.encoder
.o
].w
200 write_port
.addr
.eq(self
.cset
),
201 write_port
.data
.eq(Cat(1, self
.data_i
, self
.tag
))
205 with m
.FSM() as fsm_write
:
206 with m
.State("READY"):
207 m
.d
.comb
+= self
.ready
.eq(0)
209 m
.next
="FINISHED_WRITE"
210 with m
.State("FINISHED_WRITE"):
211 m
.d
.comb
+= self
.ready
.eq(1)
212 plru_entry
= self
.plru_array
[self
.cset
]
213 m
.d
.sync
+= plru_entry
.eq(self
.plru
.plru_tree_o
)
217 def elaborate(self
, platform
=None):
220 m
.submodules
.PLRU
= self
.plru
221 m
.submodules
.AddressEncoder
= self
.encoder
222 for i
, mem
in enumerate(self
.mem_array
):
223 setattr(m
.submodules
, "mem%d" % i
, mem
)
225 # do these all the time?
227 self
.hit
.eq(self
.encoder
.single_match
),
228 self
.multiple_hit
.eq(self
.encoder
.multiple_match
),
231 for mem
in self
.mem_array
:
232 m
.d
.comb
+= mem
.cset
.eq(self
.cset
)
233 m
.d
.comb
+= mem
.tag
.eq(self
.tag
)
235 with m
.If(self
.enable
):
236 with m
.Switch(self
.command
):
237 # Search all sets at a particular tag
242 # Maybe catch multiple tags write here?
247 return [self
.enable
, self
.command
, self
.cset
, self
.tag
, self
.data_i
,
248 self
.ready
, self
.hit
, self
.multiple_hit
, self
.data_o
]
250 if __name__
== '__main__':
251 sac
= SetAssociativeCache(4, 4, 4, 4)
252 vl
= rtlil
.convert(sac
)
253 with
open("SetAssociativeCache.il", "w") as f
: