1 from nmigen
.compat
.sim
import run_simulation
2 from nmigen
.cli
import verilog
, rtlil
3 from nmigen
import Module
, Const
, Signal
, Array
, Cat
, Elaboratable
5 from regfile
.regfile
import RegFileArray
, treereduce
6 from scoreboard
.fn_unit
import IntFnUnit
, FPFnUnit
, LDFnUnit
, STFnUnit
7 from scoreboard
.fu_fu_matrix
import FUFUDepMatrix
8 from scoreboard
.fu_reg_matrix
import FURegDepMatrix
9 from scoreboard
.global_pending
import GlobalPending
10 from scoreboard
.group_picker
import GroupPicker
11 from scoreboard
.issue_unit
import IntFPIssueUnit
13 from compalu
import ComputationUnitNoDelay
15 from alu_hier
import ALU
16 from nmutil
.latch
import SRLatch
19 class Scoreboard(Elaboratable
):
20 def __init__(self
, rwid
, n_regs
):
23 * :rwid: bit width of register file(s) - both FP and INT
24 * :n_regs: depth of register file(s) - number of FP and INT regs
30 self
.intregs
= RegFileArray(rwid
, n_regs
)
31 self
.fpregs
= RegFileArray(rwid
, n_regs
)
34 self
.int_store_i
= Signal(reset_less
=True) # instruction is a store
35 self
.int_dest_i
= Signal(max=n_regs
, reset_less
=True) # Dest R# in
36 self
.int_src1_i
= Signal(max=n_regs
, reset_less
=True) # oper1 R# in
37 self
.int_src2_i
= Signal(max=n_regs
, reset_less
=True) # oper2 R# in
39 self
.issue_o
= Signal(reset_less
=True) # instruction was accepted
41 def elaborate(self
, platform
):
44 m
.submodules
.intregs
= self
.intregs
45 m
.submodules
.fpregs
= self
.fpregs
48 int_dest
= self
.intregs
.write_port("dest")
49 int_src1
= self
.intregs
.read_port("src1")
50 int_src2
= self
.intregs
.read_port("src2")
52 fp_dest
= self
.fpregs
.write_port("dest")
53 fp_src1
= self
.fpregs
.read_port("src1")
54 fp_src2
= self
.fpregs
.read_port("src2")
59 m
.submodules
.comp1
= comp1
= ComputationUnitNoDelay(self
.rwid
, 1, add
)
60 m
.submodules
.comp2
= comp2
= ComputationUnitNoDelay(self
.rwid
, 1, sub
)
61 int_alus
= [comp1
, comp2
]
63 m
.d
.comb
+= comp1
.oper_i
.eq(Const(0)) # temporary/experiment: op=add
64 m
.d
.comb
+= comp2
.oper_i
.eq(Const(1)) # temporary/experiment: op=sub
72 for i
, a
in enumerate(int_alus
):
73 # set up Integer Function Unit, add to module (and python list)
74 fu
= IntFnUnit(self
.n_regs
, shadow_wid
=0)
75 setattr(m
.submodules
, "intfu%d" % i
, fu
)
77 # collate the read/write pending vectors (to go into global pending)
78 int_src1_pend_v
.append(fu
.src1_pend_o
)
79 int_src2_pend_v
.append(fu
.src2_pend_o
)
80 int_rd_pend_v
.append(fu
.int_rd_pend_o
)
81 int_wr_pend_v
.append(fu
.int_wr_pend_o
)
84 # Count of number of FUs
86 n_fp_fus
= 0 # for now
88 n_fus
= n_int_fus
+ n_fp_fus
# plus FP FUs
90 # XXX replaced by array of FUs? *FnUnit
91 # # Integer FU-FU Dep Matrix
92 # m.submodules.intfudeps = FUFUDepMatrix(n_int_fus, n_int_fus)
93 # Integer FU-Reg Dep Matrix
94 # intregdeps = FURegDepMatrix(self.n_regs, n_int_fus)
95 # m.submodules.intregdeps = intregdeps
97 # Integer Priority Picker 1: Adder + Subtractor
98 intpick1
= GroupPicker(2) # picks between add and sub
99 m
.submodules
.intpick1
= intpick1
101 # Global Pending Vectors (INT and FP)
102 # NOTE: number of vectors is NOT same as number of FUs.
103 g_int_src1_pend_v
= GlobalPending(self
.n_regs
, int_src1_pend_v
)
104 g_int_src2_pend_v
= GlobalPending(self
.n_regs
, int_src2_pend_v
)
105 g_int_rd_pend_v
= GlobalPending(self
.n_regs
, int_rd_pend_v
)
106 g_int_wr_pend_v
= GlobalPending(self
.n_regs
, int_wr_pend_v
)
107 m
.submodules
.g_int_src1_pend_v
= g_int_src1_pend_v
108 m
.submodules
.g_int_src2_pend_v
= g_int_src2_pend_v
109 m
.submodules
.g_int_rd_pend_v
= g_int_rd_pend_v
110 m
.submodules
.g_int_wr_pend_v
= g_int_wr_pend_v
113 issueunit
= IntFPIssueUnit(self
.n_regs
, n_int_fus
, n_fp_fus
)
114 m
.submodules
.issueunit
= issueunit
117 # ok start wiring things together...
118 # "now hear de word of de looord... dem bones dem bones dem dryy bones"
119 # https://www.youtube.com/watch?v=pYb8Wm6-QfA
123 # Issue Unit is where it starts. set up some in/outs for this module
125 m
.d
.comb
+= [issueunit
.i
.store_i
.eq(self
.int_store_i
),
126 issueunit
.i
.dest_i
.eq(self
.int_dest_i
),
127 issueunit
.i
.src1_i
.eq(self
.int_src1_i
),
128 issueunit
.i
.src2_i
.eq(self
.int_src2_i
),
129 self
.issue_o
.eq(issueunit
.issue_o
)
131 self
.int_insn_i
= issueunit
.i
.insn_i
# enabled by instruction decode
133 # connect global rd/wr pending vectors
134 m
.d
.comb
+= issueunit
.i
.g_wr_pend_i
.eq(g_int_wr_pend_v
.g_pend_o
)
135 # TODO: issueunit.f (FP)
137 # and int function issue / busy arrays, and dest/src1/src2
140 for i
, fu
in enumerate(il
):
141 fn_issue_l
.append(fu
.issue_i
)
142 fn_busy_l
.append(fu
.busy_o
)
143 # XXX sync, so as to stop a simulation infinite loop
144 m
.d
.comb
+= fu
.issue_i
.eq(issueunit
.i
.fn_issue_o
[i
])
145 m
.d
.comb
+= fu
.dest_i
.eq(issueunit
.i
.dest_i
)
146 m
.d
.comb
+= fu
.src1_i
.eq(issueunit
.i
.src1_i
)
147 m
.d
.comb
+= fu
.src2_i
.eq(issueunit
.i
.src2_i
)
148 m
.d
.comb
+= issueunit
.i
.busy_i
[i
].eq(fu
.busy_o
)
151 # connect Function Units
154 # Group Picker... done manually for now. TODO: cat array of pick sigs
155 m
.d
.comb
+= il
[0].go_rd_i
.eq(intpick1
.go_rd_o
[0]) # add rd
156 m
.d
.comb
+= il
[0].go_wr_i
.eq(intpick1
.go_wr_o
[0]) # add wr
158 m
.d
.comb
+= il
[1].go_rd_i
.eq(intpick1
.go_rd_o
[1]) # subtract rd
159 m
.d
.comb
+= il
[1].go_wr_i
.eq(intpick1
.go_wr_o
[1]) # subtract wr
161 # Connect INT Fn Unit global wr/rd pending
163 m
.d
.comb
+= fu
.g_int_wr_pend_i
.eq(g_int_wr_pend_v
.g_pend_o
)
164 m
.d
.comb
+= fu
.g_int_rd_pend_i
.eq(g_int_rd_pend_v
.g_pend_o
)
168 m
.d
.comb
+= intpick1
.req_rel_i
[0].eq(int_alus
[0].req_rel_o
)
169 m
.d
.comb
+= intpick1
.req_rel_i
[1].eq(int_alus
[1].req_rel_o
)
170 m
.d
.comb
+= intpick1
.readable_i
[0].eq(il
[0].int_readable_o
) # add rdable
171 m
.d
.comb
+= intpick1
.writable_i
[0].eq(il
[0].int_writable_o
) # add rdable
172 m
.d
.comb
+= intpick1
.readable_i
[1].eq(il
[1].int_readable_o
) # sub rdable
173 m
.d
.comb
+= intpick1
.writable_i
[1].eq(il
[1].int_writable_o
) # sub rdable
176 # Connect Register File(s)
178 m
.d
.comb
+= int_dest
.wen
.eq(g_int_wr_pend_v
.g_pend_o
)
179 m
.d
.comb
+= int_src1
.ren
.eq(g_int_src1_pend_v
.g_pend_o
)
180 m
.d
.comb
+= int_src2
.ren
.eq(g_int_src2_pend_v
.g_pend_o
)
182 # merge (OR) all integer FU / ALU outputs to a single value
183 # bit of a hack: treereduce needs a list with an item named "dest_o"
184 dest_o
= treereduce(int_alus
)
185 m
.d
.comb
+= int_dest
.data_i
.eq(dest_o
)
188 for i
, alu
in enumerate(int_alus
):
189 m
.d
.comb
+= alu
.go_rd_i
.eq(il
[i
].go_rd_i
) # chained from intpick
190 m
.d
.comb
+= alu
.go_wr_i
.eq(il
[i
].go_wr_i
) # chained from intpick
191 m
.d
.comb
+= alu
.issue_i
.eq(fn_issue_l
[i
])
192 #m.d.comb += fn_busy_l[i].eq(alu.busy_o) # XXX ignore, use fnissue
193 m
.d
.comb
+= alu
.src1_i
.eq(int_src1
.data_o
)
194 m
.d
.comb
+= alu
.src2_i
.eq(int_src2
.data_o
)
195 m
.d
.comb
+= il
[i
].req_rel_i
.eq(alu
.req_rel_o
) # pipe out ready
201 yield from self
.intregs
202 yield from self
.fpregs
203 yield self
.int_store_i
204 yield self
.int_dest_i
205 yield self
.int_src1_i
206 yield self
.int_src2_i
208 #yield from self.int_src1
209 #yield from self.int_dest
210 #yield from self.int_src1
211 #yield from self.int_src2
212 #yield from self.fp_dest
213 #yield from self.fp_src1
214 #yield from self.fp_src2
222 def int_instr(dut
, op
, src1
, src2
, dest
):
223 yield dut
.int_dest_i
.eq(dest
)
224 yield dut
.int_src1_i
.eq(src1
)
225 yield dut
.int_src2_i
.eq(src2
)
226 yield dut
.int_insn_i
[op
].eq(1)
228 def print_reg(dut
, rnums
):
231 reg
= yield dut
.intregs
.regs
[rnum
].reg
232 rs
.append("%x" % reg
)
233 rnums
= map(str, rnums
)
234 print ("reg %s: %s" % (','.join(rnums
), ','.join(rs
)))
236 def scoreboard_sim(dut
):
237 for i
in range(1, dut
.n_regs
):
238 yield dut
.intregs
.regs
[i
].reg
.eq(i
)
240 yield from int_instr(dut
, IADD
, 4, 3, 5)
241 yield from print_reg(dut
, [3,4,5])
243 yield from print_reg(dut
, [3,4,5])
245 yield from print_reg(dut
, [3,4,5])
247 yield from print_reg(dut
, [3,4,5])
249 yield from print_reg(dut
, [3,4,5])
253 def test_scoreboard():
254 dut
= Scoreboard(32, 8)
255 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
256 with
open("test_scoreboard.il", "w") as f
:
259 run_simulation(dut
, scoreboard_sim(dut
), vcd_name
='test_scoreboard.vcd')
262 if __name__
== '__main__':