-from nmigen.compat.sim import run_simulation
-from nmigen.cli import verilog, rtlil
-from nmigen import Module, Signal, Cat, Elaboratable
+""" Group Picker: to select an instruction that is permitted to read (or write)
+ based on the Function Unit expressing a *desire* to read (or write).
+ The job of the Group Picker is extremely simple yet extremely important.
+ It sits in front of a register file port (read or write) and stops it from
+ being corrupted. It's a "port contention selector", basically.
-class PriorityPicker(Elaboratable):
- """ implements a priority-picker. input: N bits, output: N bits
- """
- def __init__(self, wid):
- self.wid = wid
- # inputs
- self.i = Signal(wid, reset_less=True)
- self.o = Signal(wid, reset_less=True)
+ The way it works is:
- def elaborate(self, platform):
- m = Module()
+ * Function Units need to read from (or write to) the register file,
+ in order to get (or store) their operands, so they each have a signal,
+ readable (or writable), which "expresses" this need. This is an
+ *unary* encoding.
- res = []
- ni = Signal(self.wid, reset_less = True)
- m.d.comb += ni.eq(~self.i)
- for i in range(0, self.wid):
- t = Signal(reset_less = True)
- res.append(t)
- if i == 0:
- m.d.comb += t.eq(self.i[i])
- else:
- m.d.comb += t.eq(~Cat(ni[i], *self.i[:i]).bool())
+ * The Function Units also have a signal which indicates that they
+ are requesting "release" of the register file port (this because
+ in the scoreboard, readable/writable can be permanently HI even
+ if the FU is idle, whereas the "release" signal is very specifically
+ only HI if the read (or write) latch is still active)
- # we like Cat(*xxx). turn lists into concatenated bits
- m.d.comb += self.o.eq(Cat(*res))
+ * The Group Picker takes this unary encoding of the desire to read
+ (or write) and, on a priority basis, activates one *and only* one
+ of those signals, again as an unary output.
- return m
+ * Due to the way that the Computation Unit works, that signal (Go_Read
+ or Go_Write) will fire for one (and only one) cycle, and can be used
+ to enable the register file port read (or write) lines. The Go_Read/Wr
+ signal basically loops back to the Computation Unit and resets the
+ "desire-to-read/write-expressing" latch.
- def __iter__(self):
- yield self.i
- yield self.o
-
- def ports(self):
- return list(self)
+ In theory (and in practice!) the following is possible:
+
+ * Separate src1 and src2 Group Pickers. This would allow instructions
+ with only one operand to read to not block up other instructions,
+ and it would also allow 3-operand instructions to be interleaved
+ with 1 and 2 operand instructions.
+
+ * *Multiple* Group Pickers (multi-issue). This would require
+ a corresponding increase in the number of register file ports,
+ either 4R2W (or more) or by "striping" the register file into
+ split banks (a strategy best deployed on Vector Processors)
+
+"""
+
+from nmigen.compat.sim import run_simulation
+from nmigen.cli import verilog, rtlil
+from nmigen import Module, Signal, Elaboratable
+
+from nmutil.picker import PriorityPicker
class GroupPicker(Elaboratable):
# inputs
self.readable_i = Signal(wid, reset_less=True) # readable in (top)
self.writable_i = Signal(wid, reset_less=True) # writable in (top)
- self.go_rd_i = Signal(wid, reset_less=True) # go read in (top)
+ self.rd_rel_i = Signal(wid, reset_less=True) # go read in (top)
self.req_rel_i = Signal(wid, reset_less=True) # release request in (top)
# outputs
m.d.comb += wpick.i.eq(self.writable_i & self.req_rel_i)
m.d.comb += self.go_wr_o.eq(wpick.o)
- m.d.comb += rpick.i.eq(self.readable_i) #& self.go_rd_i)
+ m.d.comb += rpick.i.eq(self.readable_i & self.rd_rel_i)
m.d.comb += self.go_rd_o.eq(rpick.o)
return m
yield
yield dut.issue_i.eq(0)
yield
- yield dut.go_rd_i.eq(1)
+ yield dut.rd_rel_i.eq(1)
yield
- yield dut.go_rd_i.eq(0)
+ yield dut.rd_rel_i.eq(0)
yield
yield dut.go_wr_i.eq(1)
yield