From 23daa4a7dbe5d98b00bbd468b35e7b2f2c782ff4 Mon Sep 17 00:00:00 2001 From: Luke Kenneth Casson Leighton Date: Mon, 6 May 2019 13:44:46 +0100 Subject: [PATCH] generalise the function unit module, create some derivative classes * IntFnUnit * FPFnUnit * LDFnUnit * STFnUnit --- src/scoreboard/fn_unit.py | 180 ++++++++++++++++++++++++++++++++------ 1 file changed, 155 insertions(+), 25 deletions(-) diff --git a/src/scoreboard/fn_unit.py b/src/scoreboard/fn_unit.py index 505f1cb7..b2ef9468 100644 --- a/src/scoreboard/fn_unit.py +++ b/src/scoreboard/fn_unit.py @@ -15,19 +15,30 @@ class FnUnit(Elaboratable): load/store hold (exceptions again), and vector-element predication (once the predicate is known, which it may not be at instruction issue) + Inputs + + * :wid: register file width + * :shadow_wid: number of shadow/fail/good/go_die sets + * :n_dests: number of destination regfile(s) (index: rfile_sel_i) + * :wr_pend: if true, writable observes the g_wr_pend_i vector + otherwise observes g_rd_pend_i + notes: - * dest_i / src1_i / src2_i are in *binary*, whereas - * g_rd_pend_i / g_wr_pend_i and rd_pend_o / wr_pend_o are UNARY vectors + * dest_i / src1_i / src2_i are in *binary*, whereas... + * ...g_rd_pend_i / g_wr_pend_i and rd_pend_o / wr_pend_o are UNARY * req_rel_i (request release) is the direct equivalent of pipeline "output valid" (valid_o) * recover is a local python variable (actually go_die_o) * when shadow_wid = 0, recover and shadown are Consts (i.e. do nothing) + * wr_pend is set False for the majority of uses: however for + use in a STORE Function Unit it is set to True """ - def __init__(self, wid, shadow_wid=0, n_dests=1): + def __init__(self, wid, shadow_wid=0, n_dests=1, wr_pend=False): self.reg_width = wid self.n_dests = n_dests self.shadow_wid = shadow_wid + self.wr_pend = wr_pend # inputs if n_dests > 1: @@ -43,8 +54,9 @@ class FnUnit(Elaboratable): self.go_read_i = Signal(reset_less=True) # Go Read in (left) self.req_rel_i = Signal(reset_less=True) # request release (left) - self.g_rd_pend_i = Signal(wid, reset_less=True) # global rd (right) - self.g_wr_pend_i = Signal(wid, reset_less=True) # global wr (right) + self.g_xx_pend_i = Array(Signal(wid, reset_less=True, name="g_pend_i") \ + for i in range(n_dests)) # global rd (right) + self.g_wr_pend_i = Signal(wid, reset_less=True) # global wr (right) if shadow_wid: self.shadow_i = Signal(shadow_wid, reset_less=True) @@ -54,12 +66,12 @@ class FnUnit(Elaboratable): # outputs self.readable_o = Signal(reset_less=True) # Readable out (right) - self.xxxxable_o = Array(Signal(reset_less=True) \ - for i in range(n_dests)) # XXXXable out (right) + self.writable_o = Array(Signal(reset_less=True, name="writable_o") \ + for i in range(n_dests)) # writable out (right) self.busy_o = Signal(reset_less=True) # busy out (left) self.rd_pend_o = Signal(wid, reset_less=True) # rd pending (right) - self.wr_pend_o = Array(Signal(wid, reset_less=True) \ + self.xx_pend_o = Array(Signal(wid, reset_less=True, name="pend_o") \ for i in range(n_dests))# wr pending (right) def elaborate(self, platform): @@ -104,8 +116,13 @@ class FnUnit(Elaboratable): recover = Const(0) # selector - wr_pend_o = self.wr_pend_o[self.rfile_sel_i] - xxxxable_o = self.xxxxable_o[self.rfile_sel_i] + xx_pend_o = self.xx_pend_o[self.rfile_sel_i] + writable_o = self.writable_o[self.rfile_sel_i] + g_pend_i = self.g_xx_pend_i[self.rfile_sel_i] + + for i in range(self.n_dests): + m.d.comb += self.xx_pend_o[i].eq(0) # initialise all array + m.d.comb += self.writable_o[i].eq(0) # to zero # go_write latch: reset on go_write HI, set on issue m.d.comb += wr_l.s.eq(self.issue_i) @@ -119,9 +136,7 @@ class FnUnit(Elaboratable): m.d.comb += dest_d.i.eq(self.dest_i) m.d.comb += dest_d.n.eq(wr_l.qn) # decode is inverted m.d.comb += self.busy_o.eq(wr_l.q) # busy if set - for i in range(self.n_dests): - m.d.comb += self.wr_pend_o[i].eq(0) - m.d.comb += wr_pend_o.eq(dest_d.o) + m.d.comb += xx_pend_o.eq(dest_d.o) # src1/src2 decoder: read-pending out m.d.comb += src1_d.i.eq(self.src1_i) @@ -131,16 +146,18 @@ class FnUnit(Elaboratable): m.d.comb += self.rd_pend_o.eq(src1_d.o | src2_d.o) # readable output signal - int_g_wr = Signal(self.reg_width, reset_less=True) - m.d.comb += int_g_wr.eq(self.g_wr_pend_i & self.rd_pend_o) - m.d.comb += self.readable_o.eq(int_g_wr.bool()) + g_rd = Signal(self.reg_width, reset_less=True) + m.d.comb += g_rd.eq(self.g_wr_pend_i & self.rd_pend_o) + m.d.comb += self.readable_o.eq(g_rd.bool()) - # xxxxable output signal - int_g_rw = Signal(self.reg_width, reset_less=True) - g_rw = Signal(reset_less=True) - m.d.comb += int_g_rw.eq(self.g_rd_pend_i & wr_pend_o) - m.d.comb += g_rw.eq(~int_g_rw.bool()) - m.d.comb += xxxxable_o.eq(g_rw & rd_l.q & self.req_rel_i & shadown) + # writable output signal + g_wr_v = Signal(self.reg_width, reset_less=True) + g_wr = Signal(reset_less=True) + wo = Signal(reset_less=True) + m.d.comb += g_wr_v.eq(g_pend_i & xx_pend_o) + m.d.comb += g_wr.eq(~g_wr_v.bool()) + m.d.comb += wo.eq(g_wr & rd_l.q & self.req_rel_i & shadown) + m.d.comb += writable_o.eq(wo) return m @@ -152,16 +169,119 @@ class FnUnit(Elaboratable): yield self.go_write_i yield self.go_read_i yield self.req_rel_i - yield self.g_rd_pend_i + yield from self.g_xx_pend_i yield self.g_wr_pend_i yield self.readable_o - yield from self.xxxxable_o + yield from self.writable_o yield self.rd_pend_o - yield from self.wr_pend_o + yield from self.xx_pend_o def ports(self): return list(self) +############# ############### +# --- --- # +# --- renamed / redirected from base class --- # +# --- --- # +# --- below are convenience classes which match the names --- # +# --- of the various mitch alsup book chapter gate diagrams --- # +# --- --- # +############# ############### + + +class IntFnUnit(FnUnit): + def __init__(self, wid, shadow_wid=0): + FnUnit.__init__(self, wid, shadow_wid) + self.int_rd_pend_o = self.rd_pend_o + self.int_wr_pend_o = self.xx_pend_o[0] + self.g_int_wr_pend_i = self.g_wr_pend_i + self.g_int_rd_pend_i = self.g_xx_pend_i[0] + self.int_readable_o = self.readable_o + self.int_writable_o = self.writable_o[0] + + self.int_rd_pend_o.name = "int_rd_pend_o" + self.int_wr_pend_o.name = "int_wr_pend_o" + self.g_int_rd_pend_i.name = "g_int_rd_pend_i" + self.g_int_wr_pend_i.name = "g_int_wr_pend_i" + self.int_readable_o.name = "int_readable_o" + self.int_writable_o.name = "int_writable_o" + + +class FPFnUnit(FnUnit): + def __init__(self, wid, shadow_wid=0): + FnUnit.__init__(self, wid, shadow_wid) + self.fp_rd_pend_o = self.rd_pend_o + self.fp_wr_pend_o = self.xx_pend_o[0] + self.g_fp_wr_pend_i = self.g_wr_pend_i + self.g_fp_rd_pend_i = self.g_xx_pend_i[0] + self.fp_writable_o = self.writable_o[0] + self.fp_readable_o = self.readable_o + + self.fp_rd_pend_o.name = "fp_rd_pend_o" + self.fp_wr_pend_o.name = "fp_wr_pend_o" + self.g_fp_rd_pend_i.name = "g_fp_rd_pend_i" + self.g_fp_wr_pend_i.name = "g_fp_wr_pend_i" + self.fp_writable_o.name = "fp_writable_o" + self.fp_readable_o.name = "fp_readable_o" + + +class LDFnUnit(FnUnit): + """ number of dest selectors: 2. assumes len(int_regfile) == len(fp_regfile) + * when rfile_sel_i == 0, int_wr_pend_o is set + * when rfile_sel_i == 1, fp_wr_pend_o is set + """ + def __init__(self, wid, shadow_wid=0): + FnUnit.__init__(self, wid, shadow_wid, n_dests=2) + self.int_rd_pend_o = self.rd_pend_o + self.int_wr_pend_o = self.xx_pend_o[0] + self.fp_wr_pend_o = self.xx_pend_o[1] + self.g_int_wr_pend_i = self.g_wr_pend_i + self.g_int_rd_pend_i = self.g_xx_pend_i[0] + self.g_fp_rd_pend_i = self.g_xx_pend_i[1] + self.int_readable_o = self.readable_o + self.int_writable_o = self.writable_o[0] + self.fp_writable_o = self.writable_o[1] + + self.int_rd_pend_o.name = "int_rd_pend_o" + self.int_wr_pend_o.name = "int_wr_pend_o" + self.fp_wr_pend_o.name = "fp_wr_pend_o" + self.g_int_wr_pend_i.name = "g_int_wr_pend_i" + self.g_int_rd_pend_i.name = "g_int_rd_pend_i" + self.g_fp_rd_pend_i.name = "g_fp_rd_pend_i" + self.int_readable_o.name = "int_readable_o" + self.int_writable_o.name = "int_writable_o" + self.fp_writable_o.name = "fp_writable_o" + + +class STFnUnit(FnUnit): + """ number of dest selectors: 2. assumes len(int_regfile) == len(fp_regfile) + * wr_pend=False indicates to observe global fp write pending + * when rfile_sel_i == 0, int_wr_pend_o is set + * when rfile_sel_i == 1, fp_wr_pend_o is set + * + """ + def __init__(self, wid, shadow_wid=0): + FnUnit.__init__(self, wid, shadow_wid, n_dests=2, wr_pend=True) + self.int_rd_pend_o = self.rd_pend_o # 1st int read-pending vector + self.int2_rd_pend_o = self.xx_pend_o[0] # 2nd int read-pending vector + self.fp_rd_pend_o = self.xx_pend_o[1] # 1x FP read-pending vector + # yes overwrite FnUnit base class g_wr_pend_i vector + self.g_int_wr_pend_i = self.g_wr_pend_i = self.g_xx_pend_i[0] + self.g_fp_wr_pend_i = self.g_xx_pend_i[1] + self.int_readable_o = self.readable_o + self.int_writable_o = self.writable_o[0] + self.fp_writable_o = self.writable_o[1] + + self.int_rd_pend_o.name = "int_rd_pend_o" + self.int2_rd_pend_o.name = "int2_rd_pend_o" + self.fp_rd_pend_o.name = "fp_rd_pend_o" + self.g_int_wr_pend_i.name = "g_int_wr_pend_i" + self.g_fp_wr_pend_i.name = "g_fp_wr_pend_i" + self.int_readable_o.name = "int_readable_o" + self.int_writable_o.name = "int_writable_o" + self.fp_writable_o.name = "fp_writable_o" + + def int_fn_unit_sim(dut): yield dut.dest_i.eq(1) @@ -191,6 +311,16 @@ def test_int_fn_unit(): with open("test_fn_unit.il", "w") as f: f.write(vl) + dut = LDFnUnit(32, 2) + vl = rtlil.convert(dut, ports=dut.ports()) + with open("test_ld_fn_unit.il", "w") as f: + f.write(vl) + + dut = STFnUnit(32, 0) + vl = rtlil.convert(dut, ports=dut.ports()) + with open("test_st_fn_unit.il", "w") as f: + f.write(vl) + run_simulation(dut, int_fn_unit_sim(dut), vcd_name='test_fn_unit.vcd') if __name__ == '__main__': -- 2.30.2