add detection and disable of Instruction Wishbone based on JTAG command
[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 # detect whether the wishbone bus is enabled / disabled
38 if (hasattr(pspec, "wb_icache_en") and
39 isinstance(pspec.wb_icache_en, Signal)):
40 self.jtag_en = pspec.wb_icache_en
41 else:
42 self.jtag_en = Const(1, 1) # permanently on
43
44
45 def __iter__(self):
46 yield self.a_pc_i
47 yield self.a_stall_i
48 yield self.a_valid_i
49 yield self.f_stall_i
50 yield self.f_valid_i
51 yield self.a_busy_o
52 yield self.f_busy_o
53 yield self.f_instr_o
54 yield self.f_fetch_err_o
55 yield self.f_badaddr_o
56 for sig in self.ibus.fields.values():
57 yield sig
58
59 def ports(self):
60 return list(self)
61
62
63 class BareFetchUnit(FetchUnitInterface, Elaboratable):
64 def elaborate(self, platform):
65 m = Module()
66
67 with m.If(self.jtag_en): # for safety, JTAG can completely disable WB
68
69 ibus_rdata = Signal.like(self.ibus.dat_r)
70 with m.If(self.ibus.cyc):
71 with m.If(self.ibus.ack | self.ibus.err | ~self.f_valid_i):
72 m.d.sync += [
73 self.ibus.cyc.eq(0),
74 self.ibus.stb.eq(0),
75 self.ibus.sel.eq(0),
76 ibus_rdata.eq(self.ibus.dat_r)
77 ]
78 with m.Elif(self.a_valid_i & ~self.a_stall_i):
79 m.d.sync += [
80 self.ibus.adr.eq(self.a_pc_i[self.adr_lsbs:]),
81 self.ibus.cyc.eq(1),
82 self.ibus.stb.eq(1),
83 self.ibus.sel.eq((1<<(1<<self.adr_lsbs))-1),
84 ]
85
86 with m.If(self.ibus.cyc & self.ibus.err):
87 m.d.sync += [
88 self.f_fetch_err_o.eq(1),
89 self.f_badaddr_o.eq(self.ibus.adr)
90 ]
91 with m.Elif(~self.f_stall_i):
92 m.d.sync += self.f_fetch_err_o.eq(0)
93
94 m.d.comb += self.a_busy_o.eq(self.ibus.cyc)
95
96 with m.If(self.f_fetch_err_o):
97 m.d.comb += self.f_busy_o.eq(0)
98 with m.Else():
99 m.d.comb += [
100 self.f_busy_o.eq(self.ibus.cyc),
101 self.f_instr_o.eq(ibus_rdata)
102 ]
103
104 return m
105
106
107 class CachedFetchUnit(FetchUnitInterface, Elaboratable):
108 def __init__(self, pspec):
109 super().__init__(pspec)
110
111 self.icache_args = pspec.icache_args
112
113 self.a_flush = Signal()
114 self.f_pc = Signal(addr_wid)
115
116 def elaborate(self, platform):
117 m = Module()
118
119 icache = m.submodules.icache = L1Cache(*self.icache_args)
120
121 a_icache_select = Signal()
122
123 # Test whether the target address is inside the L1 cache region.
124 # We use bit masks in order to avoid carry chains from arithmetic
125 # comparisons. This restricts the region boundaries to powers of 2.
126
127 # TODO: minerva defaults adr_lsbs to 2. check this code
128 with m.Switch(self.a_pc_i[self.adr_lsbs:]):
129 def addr_below(limit):
130 assert limit in range(1, 2**30 + 1)
131 range_bits = log2_int(limit)
132 const_bits = 30 - range_bits
133 return "{}{}".format("0" * const_bits, "-" * range_bits)
134
135 if icache.base >= 4: # XX (1<<self.adr_lsbs?)
136 with m.Case(addr_below(icache.base >> self.adr_lsbs)):
137 m.d.comb += a_icache_select.eq(0)
138 with m.Case(addr_below(icache.limit >> self.adr_lsbs)):
139 m.d.comb += a_icache_select.eq(1)
140 with m.Default():
141 m.d.comb += a_icache_select.eq(0)
142
143 f_icache_select = Signal()
144 f_flush = Signal()
145
146 with m.If(~self.a_stall_i):
147 m.d.sync += [
148 f_icache_select.eq(a_icache_select),
149 f_flush.eq(self.a_flush),
150 ]
151
152 m.d.comb += [
153 icache.s1_addr.eq(self.a_pc_i[self.adr_lsbs:]),
154 icache.s1_flush.eq(self.a_flush),
155 icache.s1_stall.eq(self.a_stall_i),
156 icache.s1_valid.eq(self.a_valid_i & a_icache_select),
157 icache.s2_addr.eq(self.f_pc[self.adr_lsbs:]),
158 icache.s2_re.eq(Const(1)),
159 icache.s2_evict.eq(Const(0)),
160 icache.s2_valid.eq(self.f_valid_i & f_icache_select)
161 ]
162
163 iba = WishboneArbiter(self.pspec)
164 m.submodules.ibus_arbiter = iba
165 m.d.comb += iba.bus.connect(self.ibus)
166
167 icache_port = iba.port(priority=0)
168 cti = Mux(icache.bus_last, Cycle.END, Cycle.INCREMENT)
169 m.d.comb += [
170 icache_port.cyc.eq(icache.bus_re),
171 icache_port.stb.eq(icache.bus_re),
172 icache_port.adr.eq(icache.bus_addr),
173 icache_port.cti.eq(cti),
174 icache_port.bte.eq(Const(log2_int(icache.nwords) - 1)),
175 icache.bus_valid.eq(icache_port.ack),
176 icache.bus_error.eq(icache_port.err),
177 icache.bus_rdata.eq(icache_port.dat_r)
178 ]
179
180 bare_port = iba.port(priority=1)
181 bare_rdata = Signal.like(bare_port.dat_r)
182 with m.If(bare_port.cyc):
183 with m.If(bare_port.ack | bare_port.err | ~self.f_valid_i):
184 m.d.sync += [
185 bare_port.cyc.eq(0),
186 bare_port.stb.eq(0),
187 bare_port.sel.eq(0),
188 bare_rdata.eq(bare_port.dat_r)
189 ]
190 with m.Elif(~a_icache_select & self.a_valid_i & ~self.a_stall_i):
191 m.d.sync += [
192 bare_port.cyc.eq(1),
193 bare_port.stb.eq(1),
194 bare_port.sel.eq((1<<(1<<self.adr_lsbs))-1),
195 bare_port.adr.eq(self.a_pc_i[self.adr_lsbs:])
196 ]
197
198 m.d.comb += self.a_busy_o.eq(bare_port.cyc)
199
200 with m.If(self.ibus.cyc & self.ibus.err):
201 m.d.sync += [
202 self.f_fetch_err_o.eq(1),
203 self.f_badaddr_o.eq(self.ibus.adr)
204 ]
205 with m.Elif(~self.f_stall_i):
206 m.d.sync += self.f_fetch_err_o.eq(0)
207
208 with m.If(f_flush):
209 m.d.comb += self.f_busy_o.eq(~icache.s2_flush_ack)
210 with m.Elif(self.f_fetch_err_o):
211 m.d.comb += self.f_busy_o.eq(0)
212 with m.Elif(f_icache_select):
213 m.d.comb += [
214 self.f_busy_o.eq(icache.s2_miss),
215 self.f_instr_o.eq(icache.s2_rdata)
216 ]
217 with m.Else():
218 m.d.comb += [
219 self.f_busy_o.eq(bare_port.cyc),
220 self.f_instr_o.eq(bare_rdata)
221 ]
222
223 return m