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
, RegDecode
12 from scoreboard
.shadow
import ShadowMatrix
, WaWGrid
14 from compalu
import ComputationUnitNoDelay
16 from alu_hier
import ALU
17 from nmutil
.latch
import SRLatch
19 from random
import randint
22 class CompUnits(Elaboratable
):
24 def __init__(self
, rwid
, n_units
):
27 * :rwid: bit width of register file(s) - both FP and INT
28 * :n_units: number of ALUs
30 self
.n_units
= n_units
33 self
.issue_i
= Signal(n_units
, reset_less
=True)
34 self
.go_rd_i
= Signal(n_units
, reset_less
=True)
35 self
.go_wr_i
= Signal(n_units
, reset_less
=True)
36 self
.shadown_i
= Signal(n_units
, reset_less
=True)
37 self
.go_die_i
= Signal(n_units
, reset_less
=True)
38 self
.busy_o
= Signal(n_units
, reset_less
=True)
39 self
.rd_rel_o
= Signal(n_units
, reset_less
=True)
40 self
.req_rel_o
= Signal(n_units
, reset_less
=True)
42 self
.dest_o
= Signal(rwid
, reset_less
=True)
43 self
.src1_data_i
= Signal(rwid
, reset_less
=True)
44 self
.src2_data_i
= Signal(rwid
, reset_less
=True)
46 def elaborate(self
, platform
):
54 m
.submodules
.comp1
= comp1
= ComputationUnitNoDelay(self
.rwid
, 2, add
)
55 m
.submodules
.comp2
= comp2
= ComputationUnitNoDelay(self
.rwid
, 2, sub
)
56 m
.submodules
.comp3
= comp3
= ComputationUnitNoDelay(self
.rwid
, 2, mul
)
57 m
.submodules
.comp4
= comp4
= ComputationUnitNoDelay(self
.rwid
, 2, shf
)
58 int_alus
= [comp1
, comp2
, comp3
, comp4
]
60 m
.d
.comb
+= comp1
.oper_i
.eq(Const(0, 2)) # op=add
61 m
.d
.comb
+= comp2
.oper_i
.eq(Const(1, 2)) # op=sub
62 m
.d
.comb
+= comp3
.oper_i
.eq(Const(2, 2)) # op=mul
63 m
.d
.comb
+= comp4
.oper_i
.eq(Const(3, 2)) # op=shf
74 req_rel_l
.append(alu
.req_rel_o
)
75 rd_rel_l
.append(alu
.rd_rel_o
)
76 shadow_l
.append(alu
.shadown_i
)
77 godie_l
.append(alu
.go_die_i
)
78 go_wr_l
.append(alu
.go_wr_i
)
79 go_rd_l
.append(alu
.go_rd_i
)
80 issue_l
.append(alu
.issue_i
)
81 busy_l
.append(alu
.busy_o
)
82 m
.d
.comb
+= self
.rd_rel_o
.eq(Cat(*rd_rel_l
))
83 m
.d
.comb
+= self
.req_rel_o
.eq(Cat(*req_rel_l
))
84 m
.d
.comb
+= self
.busy_o
.eq(Cat(*busy_l
))
85 m
.d
.comb
+= Cat(*godie_l
).eq(self
.go_die_i
)
86 m
.d
.comb
+= Cat(*shadow_l
).eq(self
.shadown_i
)
87 m
.d
.comb
+= Cat(*go_wr_l
).eq(self
.go_wr_i
)
88 m
.d
.comb
+= Cat(*go_rd_l
).eq(self
.go_rd_i
)
89 m
.d
.comb
+= Cat(*issue_l
).eq(self
.issue_i
)
91 # connect data register input/output
93 # merge (OR) all integer FU / ALU outputs to a single value
94 # bit of a hack: treereduce needs a list with an item named "dest_o"
95 dest_o
= treereduce(int_alus
)
96 m
.d
.comb
+= self
.dest_o
.eq(dest_o
)
98 for i
, alu
in enumerate(int_alus
):
99 m
.d
.comb
+= alu
.src1_i
.eq(self
.src1_data_i
)
100 m
.d
.comb
+= alu
.src2_i
.eq(self
.src2_data_i
)
105 class FunctionUnits(Elaboratable
):
107 def __init__(self
, n_regs
, n_int_alus
):
109 self
.n_int_alus
= n_int_alus
111 self
.dest_i
= Signal(n_regs
, reset_less
=True) # Dest R# in
112 self
.src1_i
= Signal(n_regs
, reset_less
=True) # oper1 R# in
113 self
.src2_i
= Signal(n_regs
, reset_less
=True) # oper2 R# in
115 self
.g_int_rd_pend_o
= Signal(n_regs
, reset_less
=True)
116 self
.g_int_wr_pend_o
= Signal(n_regs
, reset_less
=True)
118 self
.dest_rsel_o
= Signal(n_regs
, reset_less
=True) # dest reg (bot)
119 self
.src1_rsel_o
= Signal(n_regs
, reset_less
=True) # src1 reg (bot)
120 self
.src2_rsel_o
= Signal(n_regs
, reset_less
=True) # src2 reg (bot)
122 self
.req_rel_i
= Signal(n_int_alus
, reset_less
= True)
123 self
.readable_o
= Signal(n_int_alus
, reset_less
=True)
124 self
.writable_o
= Signal(n_int_alus
, reset_less
=True)
126 self
.go_rd_i
= Signal(n_int_alus
, reset_less
=True)
127 self
.go_wr_i
= Signal(n_int_alus
, reset_less
=True)
128 self
.req_rel_o
= Signal(n_int_alus
, reset_less
=True)
129 self
.fn_issue_i
= Signal(n_int_alus
, reset_less
=True)
131 # Note: FURegs wr_pend_o is also outputted from here, for use in WaWGrid
133 def elaborate(self
, platform
):
136 n_int_fus
= self
.n_int_alus
138 # Integer FU-FU Dep Matrix
139 intfudeps
= FUFUDepMatrix(n_int_fus
, n_int_fus
)
140 m
.submodules
.intfudeps
= intfudeps
141 # Integer FU-Reg Dep Matrix
142 intregdeps
= FURegDepMatrix(n_int_fus
, self
.n_regs
)
143 m
.submodules
.intregdeps
= intregdeps
145 m
.d
.comb
+= self
.g_int_rd_pend_o
.eq(intregdeps
.rd_rsel_o
)
146 m
.d
.comb
+= self
.g_int_wr_pend_o
.eq(intregdeps
.wr_rsel_o
)
148 m
.d
.comb
+= intregdeps
.rd_pend_i
.eq(intregdeps
.rd_rsel_o
)
149 m
.d
.comb
+= intregdeps
.wr_pend_i
.eq(intregdeps
.wr_rsel_o
)
151 m
.d
.comb
+= intfudeps
.rd_pend_i
.eq(intregdeps
.rd_pend_o
)
152 m
.d
.comb
+= intfudeps
.wr_pend_i
.eq(intregdeps
.wr_pend_o
)
153 self
.wr_pend_o
= intregdeps
.wr_pend_o
# also output for use in WaWGrid
155 m
.d
.comb
+= intfudeps
.issue_i
.eq(self
.fn_issue_i
)
156 m
.d
.comb
+= intfudeps
.go_rd_i
.eq(self
.go_rd_i
)
157 m
.d
.comb
+= intfudeps
.go_wr_i
.eq(self
.go_wr_i
)
158 m
.d
.comb
+= self
.readable_o
.eq(intfudeps
.readable_o
)
159 m
.d
.comb
+= self
.writable_o
.eq(intfudeps
.writable_o
)
161 # Connect function issue / arrays, and dest/src1/src2
162 m
.d
.comb
+= intregdeps
.dest_i
.eq(self
.dest_i
)
163 m
.d
.comb
+= intregdeps
.src1_i
.eq(self
.src1_i
)
164 m
.d
.comb
+= intregdeps
.src2_i
.eq(self
.src2_i
)
166 m
.d
.comb
+= intregdeps
.go_rd_i
.eq(self
.go_rd_i
)
167 m
.d
.comb
+= intregdeps
.go_wr_i
.eq(self
.go_wr_i
)
168 m
.d
.comb
+= intregdeps
.issue_i
.eq(self
.fn_issue_i
)
170 m
.d
.comb
+= self
.dest_rsel_o
.eq(intregdeps
.dest_rsel_o
)
171 m
.d
.comb
+= self
.src1_rsel_o
.eq(intregdeps
.src1_rsel_o
)
172 m
.d
.comb
+= self
.src2_rsel_o
.eq(intregdeps
.src2_rsel_o
)
177 class Scoreboard(Elaboratable
):
178 def __init__(self
, rwid
, n_regs
):
181 * :rwid: bit width of register file(s) - both FP and INT
182 * :n_regs: depth of register file(s) - number of FP and INT regs
188 self
.intregs
= RegFileArray(rwid
, n_regs
)
189 self
.fpregs
= RegFileArray(rwid
, n_regs
)
192 self
.int_store_i
= Signal(reset_less
=True) # instruction is a store
193 self
.int_dest_i
= Signal(max=n_regs
, reset_less
=True) # Dest R# in
194 self
.int_src1_i
= Signal(max=n_regs
, reset_less
=True) # oper1 R# in
195 self
.int_src2_i
= Signal(max=n_regs
, reset_less
=True) # oper2 R# in
196 self
.reg_enable_i
= Signal(reset_less
=True) # enable reg decode
199 self
.issue_o
= Signal(reset_less
=True) # instruction was accepted
200 self
.busy_o
= Signal(reset_less
=True) # at least one CU is busy
202 # for branch speculation experiment. branch_direction = 0 if
203 # the branch hasn't been met yet. 1 indicates "success", 2 is "fail"
204 # branch_succ and branch_fail are requests to have the current
205 # instruction be dependent on the branch unit "shadow" capability.
206 self
.branch_succ_i
= Signal(reset_less
=True)
207 self
.branch_fail_i
= Signal(reset_less
=True)
208 self
.branch_direction_o
= Signal(2, reset_less
=True)
210 def elaborate(self
, platform
):
213 m
.submodules
.intregs
= self
.intregs
214 m
.submodules
.fpregs
= self
.fpregs
217 m
.d
.sync
+= self
.branch_succ_i
.eq(Const(0))
218 m
.d
.sync
+= self
.branch_fail_i
.eq(Const(0))
219 m
.d
.sync
+= self
.branch_direction_o
.eq(Const(0))
222 int_dest
= self
.intregs
.write_port("dest")
223 int_src1
= self
.intregs
.read_port("src1")
224 int_src2
= self
.intregs
.read_port("src2")
226 fp_dest
= self
.fpregs
.write_port("dest")
227 fp_src1
= self
.fpregs
.read_port("src1")
228 fp_src2
= self
.fpregs
.read_port("src2")
230 # Int ALUs and Comp Units
232 m
.submodules
.cu
= cu
= CompUnits(self
.rwid
, n_int_alus
)
233 m
.d
.comb
+= cu
.go_die_i
.eq(0)
236 m
.submodules
.intfus
= intfus
= FunctionUnits(self
.n_regs
, n_int_alus
)
238 # Count of number of FUs
239 n_int_fus
= n_int_alus
240 n_fp_fus
= 0 # for now
242 # Integer Priority Picker 1: Adder + Subtractor
243 intpick1
= GroupPicker(n_int_fus
) # picks between add, sub, mul and shf
244 m
.submodules
.intpick1
= intpick1
247 regdecode
= RegDecode(self
.n_regs
)
248 m
.submodules
.regdecode
= regdecode
249 issueunit
= IntFPIssueUnit(self
.n_regs
, n_int_fus
, n_fp_fus
)
250 m
.submodules
.issueunit
= issueunit
252 # Shadow Matrix. currently n_int_fus shadows, to be used for
253 # write-after-write hazards
254 m
.submodules
.shadows
= shadows
= ShadowMatrix(n_int_fus
, n_int_fus
)
255 # combined go_rd/wr + go_die (go_die used to reset latches)
256 go_rd_rst
= Signal(n_int_fus
, reset_less
=True)
257 go_wr_rst
= Signal(n_int_fus
, reset_less
=True)
258 # record previous instruction to cast shadow on current instruction
259 fn_issue_prev
= Signal(n_int_fus
)
260 prev_shadow
= Signal(n_int_fus
)
263 # ok start wiring things together...
264 # "now hear de word of de looord... dem bones dem bones dem dryy bones"
265 # https://www.youtube.com/watch?v=pYb8Wm6-QfA
269 # Issue Unit is where it starts. set up some in/outs for this module
271 m
.d
.comb
+= [issueunit
.i
.store_i
.eq(self
.int_store_i
),
272 regdecode
.dest_i
.eq(self
.int_dest_i
),
273 regdecode
.src1_i
.eq(self
.int_src1_i
),
274 regdecode
.src2_i
.eq(self
.int_src2_i
),
275 regdecode
.enable_i
.eq(self
.reg_enable_i
),
276 issueunit
.i
.dest_i
.eq(regdecode
.dest_o
),
277 self
.issue_o
.eq(issueunit
.issue_o
)
279 self
.int_insn_i
= issueunit
.i
.insn_i
# enabled by instruction decode
281 # connect global rd/wr pending vector (for WaW detection)
282 m
.d
.sync
+= issueunit
.i
.g_wr_pend_i
.eq(intfus
.g_int_wr_pend_o
)
283 # TODO: issueunit.f (FP)
285 # and int function issue / busy arrays, and dest/src1/src2
286 m
.d
.comb
+= intfus
.dest_i
.eq(regdecode
.dest_o
)
287 m
.d
.comb
+= intfus
.src1_i
.eq(regdecode
.src1_o
)
288 m
.d
.comb
+= intfus
.src2_i
.eq(regdecode
.src2_o
)
290 fn_issue_o
= issueunit
.i
.fn_issue_o
292 m
.d
.comb
+= intfus
.fn_issue_i
.eq(fn_issue_o
)
293 m
.d
.comb
+= issueunit
.i
.busy_i
.eq(cu
.busy_o
)
294 m
.d
.comb
+= self
.busy_o
.eq(cu
.busy_o
.bool())
297 # connect fu-fu matrix
300 # Group Picker... done manually for now.
301 go_rd_o
= intpick1
.go_rd_o
302 go_wr_o
= intpick1
.go_wr_o
303 go_rd_i
= intfus
.go_rd_i
304 go_wr_i
= intfus
.go_wr_i
305 # NOTE: connect to the shadowed versions so that they can "die" (reset)
306 m
.d
.comb
+= go_rd_i
[0:n_int_fus
].eq(go_rd_rst
[0:n_int_fus
]) # rd
307 m
.d
.comb
+= go_wr_i
[0:n_int_fus
].eq(go_wr_rst
[0:n_int_fus
]) # wr
311 m
.d
.comb
+= intpick1
.rd_rel_i
[0:n_int_fus
].eq(cu
.rd_rel_o
[0:n_int_fus
])
312 m
.d
.comb
+= intpick1
.req_rel_i
[0:n_int_fus
].eq(cu
.req_rel_o
[0:n_int_fus
])
313 int_rd_o
= intfus
.readable_o
314 int_wr_o
= intfus
.writable_o
315 m
.d
.comb
+= intpick1
.readable_i
[0:n_int_fus
].eq(int_rd_o
[0:n_int_fus
])
316 m
.d
.comb
+= intpick1
.writable_i
[0:n_int_fus
].eq(int_wr_o
[0:n_int_fus
])
322 m
.d
.comb
+= shadows
.issue_i
.eq(fn_issue_o
)
323 # these are explained in ShadowMatrix docstring, and are to be
324 # connected to the FUReg and FUFU Matrices, to get them to reset
325 # NOTE: do NOT connect these to the Computation Units. The CUs need to
326 # do something slightly different (due to the revolving-door SRLatches)
327 m
.d
.comb
+= go_rd_rst
.eq(go_rd_o | shadows
.go_die_o
)
328 m
.d
.comb
+= go_wr_rst
.eq(go_wr_o | shadows
.go_die_o
)
330 # connect shadows / go_dies to Computation Units
331 m
.d
.comb
+= cu
.shadown_i
[0:n_int_fus
].eq(shadows
.shadown_o
[0:n_int_fus
])
332 m
.d
.comb
+= cu
.go_die_i
[0:n_int_fus
].eq(shadows
.go_die_o
[0:n_int_fus
])
334 # ok connect first n_int_fu shadows to busy lines, to create an
335 # instruction-order linked-list-like arrangement, using a bit-matrix
336 # (instead of e.g. a ring buffer).
339 # when written, the shadow can be cancelled (and was good)
340 m
.d
.comb
+= shadows
.s_good_i
[0:n_int_fus
].eq(go_wr_o
[0:n_int_fus
])
342 # work out the current-activated busy unit (by recording the old one)
343 with m
.If(fn_issue_o
): # only update prev bit if instruction issued
344 m
.d
.sync
+= fn_issue_prev
.eq(fn_issue_o
)
346 # *previous* instruction shadows *current* instruction, and, obviously,
347 # if the previous is completed (!busy) don't cast the shadow!
348 m
.d
.comb
+= prev_shadow
.eq(~fn_issue_o
& fn_issue_prev
& cu
.busy_o
)
349 for i
in range(n_int_fus
):
350 m
.d
.comb
+= shadows
.shadow_i
[i
].eq(prev_shadow
)
353 # Connect Register File(s)
355 print ("intregdeps wen len", len(intfus
.dest_rsel_o
))
356 m
.d
.comb
+= int_dest
.wen
.eq(intfus
.dest_rsel_o
)
357 m
.d
.comb
+= int_src1
.ren
.eq(intfus
.src1_rsel_o
)
358 m
.d
.comb
+= int_src2
.ren
.eq(intfus
.src2_rsel_o
)
360 # connect ALUs to regfule
361 m
.d
.comb
+= int_dest
.data_i
.eq(cu
.dest_o
)
362 m
.d
.comb
+= cu
.src1_data_i
.eq(int_src1
.data_o
)
363 m
.d
.comb
+= cu
.src2_data_i
.eq(int_src2
.data_o
)
365 # connect ALU Computation Units
366 m
.d
.comb
+= cu
.go_rd_i
[0:n_int_fus
].eq(go_rd_o
[0:n_int_fus
])
367 m
.d
.comb
+= cu
.go_wr_i
[0:n_int_fus
].eq(go_wr_o
[0:n_int_fus
])
368 m
.d
.comb
+= cu
.issue_i
[0:n_int_fus
].eq(fn_issue_o
[0:n_int_fus
])
374 yield from self
.intregs
375 yield from self
.fpregs
376 yield self
.int_store_i
377 yield self
.int_dest_i
378 yield self
.int_src1_i
379 yield self
.int_src2_i
381 yield self
.branch_succ_i
382 yield self
.branch_fail_i
383 yield self
.branch_direction_o
398 def __init__(self
, rwidth
, nregs
):
400 self
.regs
= [0] * nregs
402 def op(self
, op
, src1
, src2
, dest
):
403 maxbits
= (1 << self
.rwidth
) - 1
404 src1
= self
.regs
[src1
]
405 src2
= self
.regs
[src2
]
413 val
= src1
>> (src2
& maxbits
)
415 val
= int(src1
> src2
)
417 val
= int(src1
< src2
)
419 val
= int(src1
== src2
)
421 val
= int(src1
!= src2
)
423 self
.regs
[dest
] = val
425 def setval(self
, dest
, val
):
426 self
.regs
[dest
] = val
429 for i
, val
in enumerate(self
.regs
):
430 reg
= yield dut
.intregs
.regs
[i
].reg
431 okstr
= "OK" if reg
== val
else "!ok"
432 print("reg %d expected %x received %x %s" % (i
, val
, reg
, okstr
))
434 def check(self
, dut
):
435 for i
, val
in enumerate(self
.regs
):
436 reg
= yield dut
.intregs
.regs
[i
].reg
438 print("reg %d expected %x received %x\n" % (i
, val
, reg
))
439 yield from self
.dump(dut
)
442 def int_instr(dut
, op
, src1
, src2
, dest
, branch_success
, branch_fail
):
443 for i
in range(len(dut
.int_insn_i
)):
444 yield dut
.int_insn_i
[i
].eq(0)
445 yield dut
.int_dest_i
.eq(dest
)
446 yield dut
.int_src1_i
.eq(src1
)
447 yield dut
.int_src2_i
.eq(src2
)
448 yield dut
.int_insn_i
[op
].eq(1)
449 yield dut
.reg_enable_i
.eq(1)
451 # these indicate that the instruction is to be made shadow-dependent on
452 # (either) branch success or branch fail
453 yield dut
.branch_fail_i
.eq(branch_fail
)
454 yield dut
.branch_succ_i
.eq(branch_success
)
457 def print_reg(dut
, rnums
):
460 reg
= yield dut
.intregs
.regs
[rnum
].reg
461 rs
.append("%x" % reg
)
462 rnums
= map(str, rnums
)
463 print ("reg %s: %s" % (','.join(rnums
), ','.join(rs
)))
466 def create_random_ops(n_ops
, shadowing
=False):
468 for i
in range(n_ops
):
469 src1
= randint(1, dut
.n_regs
-1)
470 src2
= randint(1, dut
.n_regs
-1)
471 dest
= randint(1, dut
.n_regs
-1)
475 instrs
.append((src1
, src2
, dest
, op
, (False, False)))
477 instrs
.append((src1
, src2
, dest
, op
))
481 def wait_for_busy_clear(dut
):
483 busy_o
= yield dut
.busy_o
490 def wait_for_issue(dut
):
492 issue_o
= yield dut
.issue_o
494 for i
in range(len(dut
.int_insn_i
)):
495 yield dut
.int_insn_i
[i
].eq(0)
496 yield dut
.reg_enable_i
.eq(0)
499 #yield from print_reg(dut, [1,2,3])
501 #yield from print_reg(dut, [1,2,3])
503 def scoreboard_branch_sim(dut
, alusim
):
505 yield dut
.int_store_i
.eq(1)
509 # set random values in the registers
510 for i
in range(1, dut
.n_regs
):
512 val
= randint(0, (1<<alusim
.rwidth
)-1)
513 yield dut
.intregs
.regs
[i
].reg
.eq(val
)
514 alusim
.setval(i
, val
)
516 # create some instructions: branches create a tree
517 insts
= create_random_ops(5)
519 src1
= randint(1, dut
.n_regs
-1)
520 src2
= randint(1, dut
.n_regs
-1)
523 branch_ok
= create_random_ops(5)
524 branch_fail
= create_random_ops(5)
526 insts
.append((src1
, src2
, (branch_ok
, branch_fail
), op
, (0, 0)))
528 # issue instruction(s)
534 (src1
, src2
, dest
, op
, (shadow_on
, shadow_off
)) = insts
.pop()
535 if branch_direction
== 1 and shadow_off
:
536 continue # branch was "success" and this is a "failed"... skip
537 if branch_direction
== 2 and shadow_on
:
538 continue # branch was "fail" and this is a "success"... skip
541 branch_ok
, branch_fail
= dest
543 # ok zip up the branch success / fail instructions and
544 # drop them into the queue, one marked "to have branch success"
545 # the other to be marked shadow branch "fail".
546 # one out of each of these will be cancelled
547 for ok
, fl
in zip(branch_ok
, branch_fail
):
548 instrs
.append((ok
[0], ok
[1], ok
[2], ok
[3], (1, 0)))
549 instrs
.append((fl
[0], fl
[1], fl
[2], fl
[3], (0, 1)))
550 print ("instr %d: (%d, %d, %d, %d)" % (i
, src1
, src2
, dest
, op
))
551 yield from int_instr(dut
, op
, src1
, src2
, dest
,
552 shadow_on
, shadow_off
)
554 yield from wait_for_issue(dut
)
555 branch_direction
= dut
.branch_direction_o
# which way branch went
557 # wait for all instructions to stop before checking
559 yield from wait_for_busy_clear(dut
)
561 for (src1
, src2
, dest
, op
, (shadow_on
, shadow_off
)) in insts
:
564 branch_ok
, branch_fail
= dest
566 branch_res
= alusim
.op(op
, src1
, src2
, dest
)
569 insts
.append(branch_ok
)
571 insts
.append(branch_fail
)
574 yield from alusim
.check(dut
)
575 yield from alusim
.dump(dut
)
578 def scoreboard_sim(dut
, alusim
):
580 yield dut
.int_store_i
.eq(1)
584 # set random values in the registers
585 for i
in range(1, dut
.n_regs
):
587 val
= randint(0, (1<<alusim
.rwidth
)-1)
588 yield dut
.intregs
.regs
[i
].reg
.eq(val
)
589 alusim
.setval(i
, val
)
591 # create some instructions (some random, some regression tests)
595 src1
= randint(1, dut
.n_regs
-1)
596 src2
= randint(1, dut
.n_regs
-1)
598 dest
= randint(1, dut
.n_regs
-1)
600 if dest
not in [src1
, src2
]:
610 instrs
.append((src1
, src2
, dest
, op
))
613 instrs
.append((2, 3, 3, 0))
614 instrs
.append((5, 3, 3, 1))
617 instrs
.append((5, 6, 2, 1))
618 instrs
.append((2, 2, 4, 0))
619 #instrs.append((2, 2, 3, 1))
622 instrs
.append((2, 1, 2, 3))
625 instrs
.append((2, 6, 2, 1))
626 instrs
.append((2, 1, 2, 0))
629 instrs
.append((1, 2, 7, 2))
630 instrs
.append((7, 1, 5, 0))
631 instrs
.append((4, 4, 1, 1))
634 instrs
.append((5, 6, 2, 2))
635 instrs
.append((1, 1, 4, 1))
636 instrs
.append((6, 5, 3, 0))
639 # Write-after-Write Hazard
640 instrs
.append( (3, 6, 7, 2) )
641 instrs
.append( (4, 4, 7, 1) )
644 # self-read/write-after-write followed by Read-after-Write
645 instrs
.append((1, 1, 1, 1))
646 instrs
.append((1, 5, 3, 0))
649 # Read-after-Write followed by self-read-after-write
650 instrs
.append((5, 6, 1, 2))
651 instrs
.append((1, 1, 1, 1))
654 # self-read-write sandwich
655 instrs
.append((5, 6, 1, 2))
656 instrs
.append((1, 1, 1, 1))
657 instrs
.append((1, 5, 3, 0))
661 instrs
.append( (5, 2, 5, 2) )
662 instrs
.append( (2, 6, 3, 0) )
663 instrs
.append( (4, 2, 2, 1) )
665 # issue instruction(s), wait for issue to be free before proceeding
666 for i
, (src1
, src2
, dest
, op
) in enumerate(instrs
):
668 print ("instr %d: (%d, %d, %d, %d)" % (i
, src1
, src2
, dest
, op
))
669 alusim
.op(op
, src1
, src2
, dest
)
670 yield from int_instr(dut
, op
, src1
, src2
, dest
, 0, 0)
672 yield from wait_for_issue(dut
)
674 # wait for all instructions to stop before checking
676 yield from wait_for_busy_clear(dut
)
679 yield from alusim
.check(dut
)
680 yield from alusim
.dump(dut
)
683 def explore_groups(dut
):
684 from nmigen
.hdl
.ir
import Fragment
685 from nmigen
.hdl
.xfrm
import LHSGroupAnalyzer
687 fragment
= dut
.elaborate(platform
=None)
688 fr
= Fragment
.get(fragment
, platform
=None)
690 groups
= LHSGroupAnalyzer()(fragment
._statements
)
695 def test_scoreboard():
696 dut
= Scoreboard(16, 8)
697 alusim
= RegSim(16, 8)
698 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
699 with
open("test_scoreboard6600.il", "w") as f
:
702 run_simulation(dut
, scoreboard_sim(dut
, alusim
),
703 vcd_name
='test_scoreboard6600.vcd')
706 if __name__
== '__main__':