f2e1a1de55e8295a04a48bcf9038489ddf63e604
3 not in any way intended for production use. connects up FunctionUnits to
4 Register Files in a brain-dead fashion that only permits one and only one
5 Function Unit to be operational.
7 the principle here is to take the Function Units, analyse their regspecs,
8 and turn their requirements for access to register file read/write ports
9 into groupings by Register File and Register File Port name.
11 under each grouping - by regfile/port - a list of Function Units that
12 need to connect to that port is created. as these are a contended
13 resource a "Broadcast Bus" per read/write port is then also created,
14 with access to it managed by a PriorityPicker.
16 the brain-dead part of this module is that even though there is no
17 conflict of access, regfile read/write hazards are *not* analysed,
18 and consequently it is safer to wait for the Function Unit to complete
19 before allowing a new instruction to proceed.
22 from nmigen
import Elaboratable
, Module
, Signal
23 from nmigen
.cli
import rtlil
25 from nmutil
.picker
import PriorityPicker
26 from nmutil
.util
import treereduce
28 from soc
.fu
.compunits
.compunits
import AllFunctionUnits
29 from soc
.regfile
.regfiles
import RegFiles
30 from soc
.decoder
.power_decoder
import create_pdecode
31 from soc
.decoder
.power_decoder2
import PowerDecode2
32 from soc
.decoder
.decode2execute1
import Data
33 from soc
.experiment
.l0_cache
import TstL0CacheBuffer
# test only
34 from soc
.config
.test
.test_loadstore
import TestMemPspec
38 # helper function for reducing a list of signals down to a parallel
40 def ortreereduce(tree
, attr
="data_o"):
41 return treereduce(tree
, operator
.or_
, lambda x
: getattr(x
, attr
))
44 # helper function to place full regs declarations first
45 def sort_fuspecs(fuspecs
):
47 for (regname
, fspec
) in fuspecs
.items():
48 if regname
.startswith("full"):
49 res
.append((regname
, fspec
))
50 for (regname
, fspec
) in fuspecs
.items():
51 if not regname
.startswith("full"):
52 res
.append((regname
, fspec
))
53 return res
# enumerate(res)
56 class NonProductionCore(Elaboratable
):
57 def __init__(self
, pspec
):
58 # single LD/ST funnel for memory access
59 self
.l0
= TstL0CacheBuffer(pspec
, n_units
=1)
60 pi
= self
.l0
.l0
.dports
[0]
62 # function units (only one each)
63 self
.fus
= AllFunctionUnits(pspec
, pilist
=[pi
])
65 # register files (yes plural)
66 self
.regs
= RegFiles()
69 pdecode
= create_pdecode()
70 self
.pdecode2
= PowerDecode2(pdecode
) # instruction decoder
72 # issue/valid/busy signalling
73 self
.ivalid_i
= self
.pdecode2
.valid
# instruction is valid
74 self
.issue_i
= Signal(reset_less
=True)
75 self
.busy_o
= Signal(name
="corebusy_o", reset_less
=True)
78 self
.bigendian_i
= self
.pdecode2
.dec
.bigendian
79 self
.raw_opcode_i
= self
.pdecode2
.dec
.raw_opcode_in
81 # start/stop and terminated signalling
82 self
.core_start_i
= Signal(reset_less
=True)
83 self
.core_stop_i
= Signal(reset_less
=True)
84 self
.core_terminated_o
= Signal(reset
=1) # indicates stopped
86 def elaborate(self
, platform
):
89 m
.submodules
.pdecode2
= dec2
= self
.pdecode2
90 m
.submodules
.fus
= self
.fus
91 m
.submodules
.l0
= l0
= self
.l0
92 self
.regs
.elaborate_into(m
, platform
)
96 # core start/stopped state
97 core_stopped
= Signal(reset
=1) # begins in stopped state
99 # start/stop signalling
100 with m
.If(self
.core_start_i
):
101 m
.d
.sync
+= core_stopped
.eq(1)
102 with m
.If(self
.core_stop_i
):
103 m
.d
.sync
+= core_stopped
.eq(0)
104 m
.d
.comb
+= self
.core_terminated_o
.eq(core_stopped
)
106 # connect up Function Units, then read/write ports
107 fu_bitdict
= self
.connect_instruction(m
, core_stopped
)
108 self
.connect_rdports(m
, fu_bitdict
)
109 self
.connect_wrports(m
, fu_bitdict
)
113 def connect_instruction(self
, m
, core_stopped
):
114 comb
, sync
= m
.d
.comb
, m
.d
.sync
118 # enable-signals for each FU, get one bit for each FU (by name)
119 fu_enable
= Signal(len(fus
), reset_less
=True)
121 for i
, funame
in enumerate(fus
.keys()):
122 fu_bitdict
[funame
] = fu_enable
[i
]
124 # only run when allowed and when instruction is valid
125 can_run
= Signal(reset_less
=True)
126 comb
+= can_run
.eq(self
.ivalid_i
& ~core_stopped
)
128 # connect up instructions. only one is enabled at any given time
129 for funame
, fu
in fus
.items():
130 fnunit
= fu
.fnunit
.value
131 enable
= Signal(name
="en_%s" % funame
, reset_less
=True)
132 comb
+= enable
.eq((dec2
.e
.do
.fn_unit
& fnunit
).bool() & can_run
)
134 # run this FunctionUnit if enabled, except if the instruction
135 # is "attn" in which case we HALT.
137 with m
.If(dec2
.e
.op
.internal_op
== InternalOp
.OP_ATTN
):
138 # check for ATTN: halt if true
139 m
.d
.sync
+= core_stopped
.eq(1)
141 # route operand, issue, busy, read flags and mask to FU
142 comb
+= fu
.oper_i
.eq_from_execute1(dec2
.e
)
143 comb
+= fu
.issue_i
.eq(self
.issue_i
)
144 comb
+= self
.busy_o
.eq(fu
.busy_o
)
145 rdmask
= dec2
.rdflags(fu
)
146 comb
+= fu
.rdmaskn
.eq(~rdmask
)
147 comb
+= fu_bitdict
[funame
].eq(enable
)
151 def connect_rdports(self
, m
, fu_bitdict
):
152 """connect read ports
154 orders the read regspecs into a dict-of-dicts, by regfile, by
155 regport name, then connects all FUs that want that regport by
156 way of a PriorityPicker.
158 comb
, sync
= m
.d
.comb
, m
.d
.sync
162 # dictionary of lists of regfile read ports
163 byregfiles_rd
, byregfiles_rdspec
= self
.get_byregfiles(True)
165 # okaay, now we need a PriorityPicker per regfile per regfile port
166 # loootta pickers... peter piper picked a pack of pickled peppers...
168 for regfile
, spec
in byregfiles_rd
.items():
169 fuspecs
= byregfiles_rdspec
[regfile
]
170 rdpickers
[regfile
] = {}
172 # for each named regfile port, connect up all FUs to that port
173 for (regname
, fspec
) in sort_fuspecs(fuspecs
):
174 print ("connect rd", regname
, fspec
)
176 # get the regfile specs for this regfile port
177 (rf
, read
, write
, wid
, fuspec
) = fspec
178 name
= "rdflag_%s_%s" % (regfile
, regname
)
179 rdflag
= Signal(name
=name
, reset_less
=True)
180 comb
+= rdflag
.eq(rf
)
182 # select the required read port. these are pre-defined sizes
183 print (rpidx
, regfile
, regs
.rf
.keys())
184 rport
= regs
.rf
[regfile
.lower()].r_ports
[rpidx
]
186 # create a priority picker to manage this port
187 rdpickers
[regfile
][rpidx
] = rdpick
= PriorityPicker(len(fuspec
))
188 setattr(m
.submodules
, "rdpick_%s_%s" % (regfile
, rpidx
), rdpick
)
190 # connect the regspec "reg select" number to this port
191 with m
.If(rdpick
.en_o
):
192 comb
+= rport
.ren
.eq(read
)
194 # connect up the FU req/go signals, and the reg-read to the FU
195 # and create a Read Broadcast Bus
196 for pi
, (funame
, fu
, idx
) in enumerate(fuspec
):
199 # connect request-read to picker input, and output to go-rd
200 fu_active
= fu_bitdict
[funame
]
201 pick
= fu
.rd_rel_o
[idx
] & fu_active
& rdflag
202 comb
+= rdpick
.i
[pi
].eq(pick
)
203 comb
+= fu
.go_rd_i
[idx
].eq(rdpick
.o
[pi
])
205 # connect regfile port to input, creating a Broadcast Bus
206 print ("reg connect widths",
207 regfile
, regname
, pi
, funame
,
208 src
.shape(), rport
.data_o
.shape())
209 comb
+= src
.eq(rport
.data_o
) # all FUs connect to same port
211 def connect_wrports(self
, m
, fu_bitdict
):
212 """connect write ports
214 orders the write regspecs into a dict-of-dicts, by regfile,
215 by regport name, then connects all FUs that want that regport
216 by way of a PriorityPicker.
218 note that the write-port wen, write-port data, and go_wr_i all need to
219 be on the exact same clock cycle. as there is a combinatorial loop bug
220 at the moment, these all use sync.
222 comb
, sync
= m
.d
.comb
, m
.d
.sync
225 # dictionary of lists of regfile write ports
226 byregfiles_wr
, byregfiles_wrspec
= self
.get_byregfiles(False)
228 # same for write ports.
229 # BLECH! complex code-duplication! BLECH!
231 for regfile
, spec
in byregfiles_wr
.items():
232 fuspecs
= byregfiles_wrspec
[regfile
]
233 wrpickers
[regfile
] = {}
234 for (regname
, fspec
) in sort_fuspecs(fuspecs
):
235 print ("connect wr", regname
, fspec
)
237 # get the regfile specs for this regfile port
238 (rf
, read
, write
, wid
, fuspec
) = fspec
240 # select the required write port. these are pre-defined sizes
241 print (regfile
, regs
.rf
.keys())
242 wport
= regs
.rf
[regfile
.lower()].w_ports
[rpidx
]
244 # create a priority picker to manage this port
245 wrpickers
[regfile
][rpidx
] = wrpick
= PriorityPicker(len(fuspec
))
246 setattr(m
.submodules
, "wrpick_%s_%s" % (regfile
, rpidx
), wrpick
)
248 # connect the regspec write "reg select" number to this port
249 # only if one FU actually requests (and is granted) the port
250 # will the write-enable be activated
251 with m
.If(wrpick
.en_o
):
252 sync
+= wport
.wen
.eq(write
)
254 sync
+= wport
.wen
.eq(0)
256 # connect up the FU req/go signals and the reg-read to the FU
257 # these are arbitrated by Data.ok signals
259 for pi
, (funame
, fu
, idx
) in enumerate(fuspec
):
260 # write-request comes from dest.ok
261 dest
= fu
.get_out(idx
)
262 name
= "wrflag_%s_%s_%d" % (funame
, regname
, idx
)
263 wrflag
= Signal(name
=name
, reset_less
=True)
264 comb
+= wrflag
.eq(dest
.ok
)
266 # connect request-read to picker input, and output to go-wr
267 fu_active
= fu_bitdict
[funame
]
268 pick
= fu
.wr
.rel
[idx
] & fu_active
#& wrflag
269 comb
+= wrpick
.i
[pi
].eq(pick
)
270 sync
+= fu
.go_wr_i
[idx
].eq(wrpick
.o
[pi
] & wrpick
.en_o
)
271 # connect regfile port to input
272 print ("reg connect widths",
273 regfile
, regname
, pi
, funame
,
274 dest
.shape(), wport
.data_i
.shape())
277 # here is where we create the Write Broadcast Bus. simple, eh?
278 sync
+= wport
.data_i
.eq(ortreereduce(wsigs
, "data"))
280 def get_byregfiles(self
, readmode
):
282 mode
= "read" if readmode
else "write"
287 # dictionary of lists of regfile ports
290 for (funame
, fu
) in fus
.items():
291 print ("%s ports for %s" % (mode
, funame
))
292 for idx
in range(fu
.n_src
if readmode
else fu
.n_dst
):
294 (regfile
, regname
, wid
) = fu
.get_in_spec(idx
)
296 (regfile
, regname
, wid
) = fu
.get_out_spec(idx
)
297 print (" %d %s %s %s" % (idx
, regfile
, regname
, str(wid
)))
299 rdflag
, read
= dec2
.regspecmap_read(regfile
, regname
)
302 rdflag
, read
= None, None
303 wrport
, write
= dec2
.regspecmap_write(regfile
, regname
)
304 if regfile
not in byregfiles
:
305 byregfiles
[regfile
] = {}
306 byregfiles_spec
[regfile
] = {}
307 if regname
not in byregfiles_spec
[regfile
]:
308 byregfiles_spec
[regfile
][regname
] = \
309 [rdflag
, read
, write
, wid
, []]
310 # here we start to create "lanes"
311 if idx
not in byregfiles
[regfile
]:
312 byregfiles
[regfile
][idx
] = []
313 fuspec
= (funame
, fu
, idx
)
314 byregfiles
[regfile
][idx
].append(fuspec
)
315 byregfiles_spec
[regfile
][regname
][4].append(fuspec
)
317 # ok just print that out, for convenience
318 for regfile
, spec
in byregfiles
.items():
319 print ("regfile %s ports:" % mode
, regfile
)
320 fuspecs
= byregfiles_spec
[regfile
]
321 for regname
, fspec
in fuspecs
.items():
322 [rdflag
, read
, write
, wid
, fuspec
] = fspec
323 print (" rf %s port %s lane: %s" % (mode
, regfile
, regname
))
324 print (" %s" % regname
, wid
, read
, write
, rdflag
)
325 for (funame
, fu
, idx
) in fuspec
:
326 fusig
= fu
.src_i
[idx
] if readmode
else fu
.dest
[idx
]
327 print (" ", funame
, fu
, idx
, fusig
)
330 return byregfiles
, byregfiles_spec
333 yield from self
.fus
.ports()
334 yield from self
.pdecode2
.ports()
335 yield from self
.l0
.ports()
342 if __name__
== '__main__':
343 pspec
= TestMemPspec(ldst_ifacetype
='testpi',
348 dut
= NonProductionCore(pspec
)
349 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
350 with
open("test_core.il", "w") as f
: