1 from nmigen
import Elaboratable
, Module
, Signal
, Record
, Const
, Mux
2 from nmigen
.utils
import log2_int
4 from soc
.minerva
.cache
import L1Cache
5 from soc
.minerva
.wishbone
import make_wb_layout
, WishboneArbiter
, Cycle
8 __all__
= ["FetchUnitInterface", "BareFetchUnit", "CachedFetchUnit"]
11 class FetchUnitInterface
:
12 def __init__(self
, 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?
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()
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
)
43 yield self
.f_fetch_err_o
44 yield self
.f_badaddr_o
45 for sig
in self
.ibus
.fields
.values():
52 class BareFetchUnit(FetchUnitInterface
, Elaboratable
):
53 def elaborate(self
, platform
):
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
):
62 ibus_rdata
.eq(self
.ibus
.dat_r
)
64 with m
.Elif(self
.a_valid_i
& ~self
.a_stall_i
):
66 self
.ibus
.adr
.eq(self
.a_pc_i
[self
.adr_lsbs
:]),
71 with m
.If(self
.ibus
.cyc
& self
.ibus
.err
):
73 self
.f_fetch_err_o
.eq(1),
74 self
.f_badaddr_o
.eq(self
.ibus
.adr
)
76 with m
.Elif(~self
.f_stall_i
):
77 m
.d
.sync
+= self
.f_fetch_err_o
.eq(0)
79 m
.d
.comb
+= self
.a_busy_o
.eq(self
.ibus
.cyc
)
81 with m
.If(self
.f_fetch_err_o
):
82 m
.d
.comb
+= self
.f_busy_o
.eq(0)
85 self
.f_busy_o
.eq(self
.ibus
.cyc
),
86 self
.f_instr_o
.eq(ibus_rdata
)
92 class CachedFetchUnit(FetchUnitInterface
, Elaboratable
):
93 def __init__(self
, pspec
):
94 super().__init
__(pspec
)
96 self
.icache_args
= pspec
.icache_args
98 self
.a_flush
= Signal()
99 self
.f_pc
= Signal(addr_wid
)
101 def elaborate(self
, platform
):
104 icache
= m
.submodules
.icache
= L1Cache(*self
.icache_args
)
106 a_icache_select
= Signal()
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.
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
)
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)
126 m
.d
.comb
+= a_icache_select
.eq(0)
128 f_icache_select
= Signal()
131 with m
.If(~self
.a_stall_i
):
133 f_icache_select
.eq(a_icache_select
),
134 f_flush
.eq(self
.a_flush
),
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
)
148 iba
= WishboneArbiter(self
.pspec
)
149 m
.submodules
.ibus_arbiter
= iba
150 m
.d
.comb
+= iba
.bus
.connect(self
.ibus
)
152 icache_port
= iba
.port(priority
=0)
153 cti
= Mux(icache
.bus_last
, Cycle
.END
, Cycle
.INCREMENT
)
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
)
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
):
172 bare_rdata
.eq(bare_port
.dat_r
)
174 with m
.Elif(~a_icache_select
& self
.a_valid_i
& ~self
.a_stall_i
):
178 bare_port
.adr
.eq(self
.a_pc_i
[self
.adr_lsbs
:])
181 m
.d
.comb
+= self
.a_busy_o
.eq(bare_port
.cyc
)
183 with m
.If(self
.ibus
.cyc
& self
.ibus
.err
):
185 self
.f_fetch_err_o
.eq(1),
186 self
.f_badaddr_o
.eq(self
.ibus
.adr
)
188 with m
.Elif(~self
.f_stall_i
):
189 m
.d
.sync
+= self
.f_fetch_err_o
.eq(0)
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
):
197 self
.f_busy_o
.eq(icache
.s2_miss
),
198 self
.f_instr_o
.eq(icache
.s2_rdata
)
202 self
.f_busy_o
.eq(bare_port
.cyc
),
203 self
.f_instr_o
.eq(bare_rdata
)