b3e536b06ce6b7394b7fc522e5aebd108deccc5e
[soc.git] / src / soc / minerva / units / fetch.py
1 from nmigen import Elaboratable, Module, Signal, Record, Const, Mux
2 from nmigen.utils import log2_int
3
4 from soc.minerva.cache import L1Cache
5 from soc.minerva.wishbone import make_wb_layout, WishboneArbiter, Cycle
6
7
8 __all__ = ["FetchUnitInterface", "BareFetchUnit", "CachedFetchUnit"]
9
10
11 class FetchUnitInterface:
12 def __init__(self, pspec):
13 self.pspec = pspec
14 self.addr_wid = pspec.addr_wid
15 if isinstance(pspec.imem_reg_wid, int):
16 self.data_wid = pspec.imem_reg_wid
17 else:
18 self.data_wid = pspec.reg_wid
19 self.adr_lsbs = log2_int(self.data_wid//8)
20 self.ibus = Record(make_wb_layout(pspec))
21 bad_wid = pspec.addr_wid - self.adr_lsbs # TODO: is this correct?
22
23 # inputs: address to fetch PC, and valid/stall signalling
24 self.a_pc_i = Signal(self.addr_wid)
25 self.a_stall_i = Signal()
26 self.a_valid_i = Signal()
27 self.f_stall_i = Signal()
28 self.f_valid_i = Signal()
29
30 # outputs: instruction (or error), and busy indicators
31 self.a_busy_o = Signal()
32 self.f_busy_o = Signal()
33 self.f_instr_o = Signal(self.data_wid)
34 self.f_fetch_err_o = Signal()
35 self.f_badaddr_o = Signal(bad_wid)
36
37 def __iter__(self):
38 yield self.a_pc_i
39 yield self.a_stall_i
40 yield self.a_valid_i
41 yield self.f_stall_i
42 yield self.f_valid_i
43 yield self.a_busy_o
44 yield self.f_busy_o
45 yield self.f_instr_o
46 yield self.f_fetch_err_o
47 yield self.f_badaddr_o
48 for sig in self.ibus.fields.values():
49 yield sig
50
51 def ports(self):
52 return list(self)
53
54
55 class BareFetchUnit(FetchUnitInterface, Elaboratable):
56 def elaborate(self, platform):
57 m = Module()
58
59 ibus_rdata = Signal.like(self.ibus.dat_r)
60 with m.If(self.ibus.cyc):
61 with m.If(self.ibus.ack | self.ibus.err | ~self.f_valid_i):
62 m.d.sync += [
63 self.ibus.cyc.eq(0),
64 self.ibus.stb.eq(0),
65 self.ibus.sel.eq(0),
66 ibus_rdata.eq(self.ibus.dat_r)
67 ]
68 with m.Elif(self.a_valid_i & ~self.a_stall_i):
69 m.d.sync += [
70 self.ibus.adr.eq(self.a_pc_i[self.adr_lsbs:]),
71 self.ibus.cyc.eq(1),
72 self.ibus.stb.eq(1),
73 self.ibus.sel.eq((1<<(1<<self.adr_lsbs))-1),
74 ]
75
76 with m.If(self.ibus.cyc & self.ibus.err):
77 m.d.sync += [
78 self.f_fetch_err_o.eq(1),
79 self.f_badaddr_o.eq(self.ibus.adr)
80 ]
81 with m.Elif(~self.f_stall_i):
82 m.d.sync += self.f_fetch_err_o.eq(0)
83
84 m.d.comb += self.a_busy_o.eq(self.ibus.cyc)
85
86 with m.If(self.f_fetch_err_o):
87 m.d.comb += self.f_busy_o.eq(0)
88 with m.Else():
89 m.d.comb += [
90 self.f_busy_o.eq(self.ibus.cyc),
91 self.f_instr_o.eq(ibus_rdata)
92 ]
93
94 return m
95
96
97 class CachedFetchUnit(FetchUnitInterface, Elaboratable):
98 def __init__(self, pspec):
99 super().__init__(pspec)
100
101 self.icache_args = pspec.icache_args
102
103 self.a_flush = Signal()
104 self.f_pc = Signal(addr_wid)
105
106 def elaborate(self, platform):
107 m = Module()
108
109 icache = m.submodules.icache = L1Cache(*self.icache_args)
110
111 a_icache_select = Signal()
112
113 # Test whether the target address is inside the L1 cache region.
114 # We use bit masks in order to avoid carry chains from arithmetic
115 # comparisons. This restricts the region boundaries to powers of 2.
116
117 # TODO: minerva defaults adr_lsbs to 2. check this code
118 with m.Switch(self.a_pc_i[self.adr_lsbs:]):
119 def addr_below(limit):
120 assert limit in range(1, 2**30 + 1)
121 range_bits = log2_int(limit)
122 const_bits = 30 - range_bits
123 return "{}{}".format("0" * const_bits, "-" * range_bits)
124
125 if icache.base >= 4: # XX (1<<self.adr_lsbs?)
126 with m.Case(addr_below(icache.base >> self.adr_lsbs)):
127 m.d.comb += a_icache_select.eq(0)
128 with m.Case(addr_below(icache.limit >> self.adr_lsbs)):
129 m.d.comb += a_icache_select.eq(1)
130 with m.Default():
131 m.d.comb += a_icache_select.eq(0)
132
133 f_icache_select = Signal()
134 f_flush = Signal()
135
136 with m.If(~self.a_stall_i):
137 m.d.sync += [
138 f_icache_select.eq(a_icache_select),
139 f_flush.eq(self.a_flush),
140 ]
141
142 m.d.comb += [
143 icache.s1_addr.eq(self.a_pc_i[self.adr_lsbs:]),
144 icache.s1_flush.eq(self.a_flush),
145 icache.s1_stall.eq(self.a_stall_i),
146 icache.s1_valid.eq(self.a_valid_i & a_icache_select),
147 icache.s2_addr.eq(self.f_pc[self.adr_lsbs:]),
148 icache.s2_re.eq(Const(1)),
149 icache.s2_evict.eq(Const(0)),
150 icache.s2_valid.eq(self.f_valid_i & f_icache_select)
151 ]
152
153 iba = WishboneArbiter(self.pspec)
154 m.submodules.ibus_arbiter = iba
155 m.d.comb += iba.bus.connect(self.ibus)
156
157 icache_port = iba.port(priority=0)
158 cti = Mux(icache.bus_last, Cycle.END, Cycle.INCREMENT)
159 m.d.comb += [
160 icache_port.cyc.eq(icache.bus_re),
161 icache_port.stb.eq(icache.bus_re),
162 icache_port.adr.eq(icache.bus_addr),
163 icache_port.cti.eq(cti),
164 icache_port.bte.eq(Const(log2_int(icache.nwords) - 1)),
165 icache.bus_valid.eq(icache_port.ack),
166 icache.bus_error.eq(icache_port.err),
167 icache.bus_rdata.eq(icache_port.dat_r)
168 ]
169
170 bare_port = iba.port(priority=1)
171 bare_rdata = Signal.like(bare_port.dat_r)
172 with m.If(bare_port.cyc):
173 with m.If(bare_port.ack | bare_port.err | ~self.f_valid_i):
174 m.d.sync += [
175 bare_port.cyc.eq(0),
176 bare_port.stb.eq(0),
177 bare_port.sel.eq(0),
178 bare_rdata.eq(bare_port.dat_r)
179 ]
180 with m.Elif(~a_icache_select & self.a_valid_i & ~self.a_stall_i):
181 m.d.sync += [
182 bare_port.cyc.eq(1),
183 bare_port.stb.eq(1),
184 bare_port.sel.eq((1<<(1<<self.adr_lsbs))-1),
185 bare_port.adr.eq(self.a_pc_i[self.adr_lsbs:])
186 ]
187
188 m.d.comb += self.a_busy_o.eq(bare_port.cyc)
189
190 with m.If(self.ibus.cyc & self.ibus.err):
191 m.d.sync += [
192 self.f_fetch_err_o.eq(1),
193 self.f_badaddr_o.eq(self.ibus.adr)
194 ]
195 with m.Elif(~self.f_stall_i):
196 m.d.sync += self.f_fetch_err_o.eq(0)
197
198 with m.If(f_flush):
199 m.d.comb += self.f_busy_o.eq(~icache.s2_flush_ack)
200 with m.Elif(self.f_fetch_err_o):
201 m.d.comb += self.f_busy_o.eq(0)
202 with m.Elif(f_icache_select):
203 m.d.comb += [
204 self.f_busy_o.eq(icache.s2_miss),
205 self.f_instr_o.eq(icache.s2_rdata)
206 ]
207 with m.Else():
208 m.d.comb += [
209 self.f_busy_o.eq(bare_port.cyc),
210 self.f_instr_o.eq(bare_rdata)
211 ]
212
213 return m