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