from scoreboard.group_picker import GroupPicker
from scoreboard.issue_unit import IssueUnitGroup, IssueUnitArray, RegDecode
from scoreboard.shadow import ShadowMatrix, BranchSpeculationRecord
+from scoreboard.instruction_q import Instruction, InstructionQ
from compalu import ComputationUnitNoDelay
from alu_hier import ALU, BranchALU
from nmutil.latch import SRLatch
+from nmutil.nmoperator import eq
from random import randint, seed
from copy import deepcopy
+from math import log
+
+
+class Memory(Elaboratable):
+ def __init__(self, regwid, addrw):
+ self.ddepth = regwid/8
+ depth = (1<<addrw) / self.ddepth
+ self.adr = Signal(addrw)
+ self.dat_r = Signal(regwid)
+ self.dat_w = Signal(regwid)
+ self.we = Signal()
+ self.mem = Memory(width=regwid, depth=depth, init=range(0, depth))
+
+ def elaborate(self, platform):
+ m = Module()
+ m.submodules.rdport = rdport = self.mem.read_port()
+ m.submodules.wrport = wrport = self.mem.write_port()
+ m.d.comb += [
+ rdport.addr.eq(self.adr[self.ddepth:]), # ignore low bits
+ self.dat_r.eq(rdport.data),
+ wrport.addr.eq(self.adr),
+ wrport.data.eq(self.dat_w),
+ wrport.en.eq(self.we),
+ ]
+ return m
+
+
+class MemSim:
+ def __init__(self, regwid, addrw):
+ self.regwid = regwid
+ self.ddepth = regwid//8
+ depth = (1<<addrw) // self.ddepth
+ self.mem = list(range(0, depth))
+
+ def ld(self, addr):
+ return self.mem[addr>>self.ddepth]
+
+ def st(self, addr, data):
+ self.mem[addr>>self.ddepth] = data & ((1<<self.regwid)-1)
class CompUnitsBase(Elaboratable):
# inputs
self.oper_i = Signal(opwid, reset_less=True)
+ self.imm_i = Signal(rwid, reset_less=True)
# Int ALUs
add = ALU(rwid)
units = []
for alu in [add, sub, mul, shf]:
- units.append(ComputationUnitNoDelay(rwid, 2, alu))
+ aluopwid = 3 # extra bit for immediate mode
+ units.append(ComputationUnitNoDelay(rwid, aluopwid, alu))
CompUnitsBase.__init__(self, rwid, units)
m = CompUnitsBase.elaborate(self, platform)
comb = m.d.comb
- # hand the same operation to all units
+ # hand the same operation to all units, only lower 2 bits though
for alu in self.units:
- comb += alu.oper_i.eq(self.oper_i)
- #comb += self.units[0].oper_i.eq(Const(0, 2)) # op=add
- #comb += self.units[1].oper_i.eq(Const(1, 2)) # op=sub
- #comb += self.units[2].oper_i.eq(Const(2, 2)) # op=mul
- #comb += self.units[3].oper_i.eq(Const(3, 2)) # op=shf
+ comb += alu.oper_i[0:3].eq(self.oper_i)
+ comb += alu.imm_i.eq(self.imm_i)
return m
# hand the same operation to all units
for alu in self.units:
comb += alu.oper_i.eq(self.oper_i)
- #comb += self.br1.oper_i.eq(Const(4, 3)) # op=bgt
return m
self.intregs = RegFileArray(rwid, n_regs)
self.fpregs = RegFileArray(rwid, n_regs)
+ # issue q needs to get at these
+ self.aluissue = IssueUnitGroup(4)
+ self.brissue = IssueUnitGroup(1)
+ # and these
+ self.alu_oper_i = Signal(4, reset_less=True)
+ self.alu_imm_i = Signal(rwid, reset_less=True)
+ self.br_oper_i = Signal(4, reset_less=True)
+
# inputs
self.int_dest_i = Signal(max=n_regs, reset_less=True) # Dest R# in
self.int_src1_i = Signal(max=n_regs, reset_less=True) # oper1 R# in
# Int ALUs and Comp Units
n_int_alus = 5
- cua = CompUnitALUs(self.rwid, 2)
+ cua = CompUnitALUs(self.rwid, 3)
cub = CompUnitBR(self.rwid, 2)
m.submodules.cu = cu = CompUnitsBase(self.rwid, [cua, cub])
bgt = cub.bgt # get at the branch computation unit
# INT/FP Issue Unit
regdecode = RegDecode(self.n_regs)
m.submodules.regdecode = regdecode
- aluissue = IssueUnitGroup(4)
- brissue = IssueUnitGroup(1)
- issueunit = IssueUnitArray([aluissue, brissue])
+ issueunit = IssueUnitArray([self.aluissue, self.brissue])
m.submodules.issueunit = issueunit
# Shadow Matrix. currently n_intfus shadows, to be used for
m.submodules.bshadow = bshadow = ShadowMatrix(n_intfus, 1, False)
# record previous instruction to cast shadow on current instruction
- fn_issue_prev = Signal(n_intfus)
prev_shadow = Signal(n_intfus)
# Branch Speculation recorder. tracks the success/fail state as
self.issue_o.eq(issueunit.issue_o)
]
- # take these to outside (for testing)
- self.aluissue = aluissue
- self.brissue = brissue
- self.alu_oper_i = cua.oper_i
- self.br_oper_i = cub.oper_i
+ # take these to outside (issue needs them)
+ comb += cua.oper_i.eq(self.alu_oper_i)
+ comb += cua.imm_i.eq(self.alu_imm_i)
+ comb += cub.oper_i.eq(self.br_oper_i)
# TODO: issueunit.f (FP)
for i in range(n_intfus):
comb += shadows.s_good_i[i][0:n_intfus].eq(go_wr_o[0:n_intfus])
- # work out the current-activated busy unit (by recording the old one)
- with m.If(fn_issue_o): # only update prev bit if instruction issued
- sync += fn_issue_prev.eq(fn_issue_o)
-
# *previous* instruction shadows *current* instruction, and, obviously,
# if the previous is completed (!busy) don't cast the shadow!
comb += prev_shadow.eq(~fn_issue_o & cu.busy_o)
return m
-
def __iter__(self):
yield from self.intregs
yield from self.fpregs
def ports(self):
return list(self)
+
+class IssueToScoreboard(Elaboratable):
+
+ def __init__(self, qlen, n_in, n_out, rwid, opwid, n_regs):
+ self.qlen = qlen
+ self.n_in = n_in
+ self.n_out = n_out
+ self.rwid = rwid
+ self.opw = opwid
+ self.n_regs = n_regs
+
+ mqbits = (int(log(qlen) / log(2))+2, False)
+ self.p_add_i = Signal(mqbits) # instructions to add (from data_i)
+ self.p_ready_o = Signal() # instructions were added
+ self.data_i = Instruction.nq(n_in, "data_i", rwid, opwid)
+
+ self.busy_o = Signal(reset_less=True) # at least one CU is busy
+ self.qlen_o = Signal(mqbits, reset_less=True)
+
+ def elaborate(self, platform):
+ m = Module()
+ comb = m.d.comb
+ sync = m.d.sync
+
+ iq = InstructionQ(self.rwid, self.opw, self.qlen, self.n_in, self.n_out)
+ sc = Scoreboard(self.rwid, self.n_regs)
+ m.submodules.iq = iq
+ m.submodules.sc = sc
+
+ # get at the regfile for testing
+ self.intregs = sc.intregs
+
+ # and the "busy" signal and instruction queue length
+ comb += self.busy_o.eq(sc.busy_o)
+ comb += self.qlen_o.eq(iq.qlen_o)
+
+ # link up instruction queue
+ comb += iq.p_add_i.eq(self.p_add_i)
+ comb += self.p_ready_o.eq(iq.p_ready_o)
+ for i in range(self.n_in):
+ comb += eq(iq.data_i[i], self.data_i[i])
+
+ # take instruction and process it. note that it's possible to
+ # "inspect" the queue contents *without* actually removing the
+ # items. items are only removed when the
+
+ # in "waiting" state
+ wait_issue_br = Signal()
+ wait_issue_alu = Signal()
+
+ with m.If(wait_issue_br | wait_issue_alu):
+ # set instruction pop length to 1 if the unit accepted
+ with m.If(wait_issue_br & (sc.brissue.fn_issue_o != 0)):
+ with m.If(iq.qlen_o != 0):
+ comb += iq.n_sub_i.eq(1)
+ with m.If(wait_issue_alu & (sc.aluissue.fn_issue_o != 0)):
+ with m.If(iq.qlen_o != 0):
+ comb += iq.n_sub_i.eq(1)
+
+ # see if some instruction(s) are here. note that this is
+ # "inspecting" the in-place queue. note also that on the
+ # cycle following "waiting" for fn_issue_o to be set, the
+ # "resetting" done above (insn_i=0) could be re-ASSERTed.
+ with m.If(iq.qlen_o != 0):
+ # get the operands and operation
+ imm = iq.data_o[0].imm_i
+ dest = iq.data_o[0].dest_i
+ src1 = iq.data_o[0].src1_i
+ src2 = iq.data_o[0].src2_i
+ op = iq.data_o[0].oper_i
+ opi = iq.data_o[0].opim_i # immediate set
+
+ # set the src/dest regs
+ comb += sc.int_dest_i.eq(dest)
+ comb += sc.int_src1_i.eq(src1)
+ comb += sc.int_src2_i.eq(src2)
+ comb += sc.reg_enable_i.eq(1) # enable the regfile
+
+ # choose a Function-Unit-Group
+ with m.If((op & (0x3<<2)) != 0): # branch
+ comb += sc.brissue.insn_i.eq(1)
+ comb += sc.br_oper_i.eq(op & 0x3)
+ comb += wait_issue_br.eq(1)
+ with m.Else(): # alu
+ comb += sc.aluissue.insn_i.eq(1)
+ comb += sc.alu_oper_i.eq(Cat(op[0:2], opi))
+ comb += sc.alu_imm_i.eq(imm)
+ comb += wait_issue_alu.eq(1)
+
+ # XXX TODO
+ # these indicate that the instruction is to be made
+ # shadow-dependent on
+ # (either) branch success or branch fail
+ #yield sc.branch_fail_i.eq(branch_fail)
+ #yield sc.branch_succ_i.eq(branch_success)
+
+ return m
+
+ def __iter__(self):
+ yield self.p_ready_o
+ for o in self.data_i:
+ yield from list(o)
+ yield self.p_add_i
+
+ def ports(self):
+ return list(self)
+
+
IADD = 0
ISUB = 1
IMUL = 2
self.rwidth = rwidth
self.regs = [0] * nregs
- def op(self, op, src1, src2, dest):
+ def op(self, op, op_imm, imm, src1, src2, dest):
maxbits = (1 << self.rwidth) - 1
src1 = self.regs[src1] & maxbits
- src2 = self.regs[src2] & maxbits
+ if op_imm:
+ src2 = imm
+ else:
+ src2 = self.regs[src2] & maxbits
if op == IADD:
val = src1 + src2
elif op == ISUB:
yield from self.dump(dut)
assert False
-def int_instr(dut, op, src1, src2, dest, branch_success, branch_fail):
+def instr_q(dut, op, op_imm, imm, src1, src2, dest,
+ branch_success, branch_fail):
+ instrs = [{'oper_i': op, 'dest_i': dest, 'imm_i': imm, 'opim_i': op_imm,
+ 'src1_i': src1, 'src2_i': src2}]
+
+ sendlen = 1
+ for idx in range(sendlen):
+ yield from eq(dut.data_i[idx], instrs[idx])
+ di = yield dut.data_i[idx]
+ print ("senddata %d %x" % (idx, di))
+ yield dut.p_add_i.eq(sendlen)
+ yield
+ o_p_ready = yield dut.p_ready_o
+ while not o_p_ready:
+ yield
+ o_p_ready = yield dut.p_ready_o
+
+ yield dut.p_add_i.eq(0)
+
+
+def int_instr(dut, op, imm, src1, src2, dest, branch_success, branch_fail):
yield from disable_issue(dut)
yield dut.int_dest_i.eq(dest)
yield dut.int_src1_i.eq(src1)
else:
yield dut.aluissue.insn_i.eq(1)
yield dut.alu_oper_i.eq(Const(op & 0x3, 2))
+ yield dut.alu_imm_i.eq(imm)
dut_issue = dut.aluissue
yield dut.reg_enable_i.eq(1)
for i in range(n_ops):
src1 = randint(1, dut.n_regs-1)
src2 = randint(1, dut.n_regs-1)
+ imm = randint(1, (1<<dut.rwid)-1)
dest = randint(1, dut.n_regs-1)
op = randint(0, max_opnums)
+ opi = 0 if randint(0, 2) else 1 # set true if random is nonzero
if shadowing:
- insts.append((src1, src2, dest, op, (0, 0)))
+ insts.append((src1, src2, dest, op, opi, imm, (0, 0)))
else:
- insts.append((src1, src2, dest, op))
+ insts.append((src1, src2, dest, op, opi, imm))
return insts
seed(0)
- for i in range(20):
+ for i in range(1):
# set random values in the registers
for i in range(1, dut.n_regs):
# create some instructions (some random, some regression tests)
instrs = []
- if True:
- instrs = create_random_ops(dut, 10, True, 4)
+ if False:
+ instrs = create_random_ops(dut, 15, True, 4)
+
+ if False:
+ instrs.append( (1, 2, 2, 1, 1, 20, (0, 0)) )
if False:
instrs.append( (7, 3, 2, 4, (0, 0)) )
instrs.append( (7, 6, 6, 2, (0, 0)) )
instrs.append( (1, 7, 2, 2, (0, 0)) )
-
if False:
- instrs.append((2, 3, 3, 0, (0, 0)))
- instrs.append((5, 3, 3, 1, (0, 0)))
- instrs.append((3, 5, 5, 2, (0, 0)))
- instrs.append((5, 3, 3, 3, (0, 0)))
- instrs.append((3, 5, 5, 0, (0, 0)))
+ instrs.append((2, 3, 3, 0, 0, 0, (0, 0)))
+ instrs.append((5, 3, 3, 1, 0, 0, (0, 0)))
+ instrs.append((3, 5, 5, 2, 0, 0, (0, 0)))
+ instrs.append((5, 3, 3, 3, 0, 0, (0, 0)))
+ instrs.append((3, 5, 5, 0, 0, 0, (0, 0)))
+
+ if True:
+ instrs.append( (3, 3, 4, 0, 0, 13979, (0, 0)))
+ instrs.append( (6, 4, 1, 2, 0, 40976, (0, 0)))
+ instrs.append( (1, 4, 7, 4, 1, 23652, (0, 0)))
if False:
instrs.append((5, 6, 2, 1))
instrs.append((4, 2, 1, 2, (1, 0)))
if False:
- instrs.append( (4, 3, 5, 1, (0, 0)) )
- instrs.append( (5, 2, 3, 1, (0, 0)) )
- instrs.append( (7, 1, 5, 2, (0, 0)) )
- instrs.append( (5, 6, 6, 4, (0, 0)) )
- instrs.append( (7, 5, 2, 2, (1, 0)) )
- instrs.append( (1, 7, 5, 0, (0, 1)) )
- instrs.append( (1, 6, 1, 2, (1, 0)) )
- instrs.append( (1, 6, 7, 3, (0, 0)) )
- instrs.append( (6, 7, 7, 0, (0, 0)) )
+ instrs.append( (4, 3, 5, 1, 0, (0, 0)) )
+ instrs.append( (5, 2, 3, 1, 0, (0, 0)) )
+ instrs.append( (7, 1, 5, 2, 0, (0, 0)) )
+ instrs.append( (5, 6, 6, 4, 0, (0, 0)) )
+ instrs.append( (7, 5, 2, 2, 0, (1, 0)) )
+ instrs.append( (1, 7, 5, 0, 0, (0, 1)) )
+ instrs.append( (1, 6, 1, 2, 0, (1, 0)) )
+ instrs.append( (1, 6, 7, 3, 0, (0, 0)) )
+ instrs.append( (6, 7, 7, 0, 0, (0, 0)) )
# issue instruction(s), wait for issue to be free before proceeding
- for i, (src1, src2, dest, op, (br_ok, br_fail)) in enumerate(instrs):
+ for i, instr in enumerate(instrs):
+ src1, src2, dest, op, opi, imm, (br_ok, br_fail) = instr
- print ("instr %d: (%d, %d, %d, %d)" % (i, src1, src2, dest, op))
- alusim.op(op, src1, src2, dest)
- yield from int_instr(dut, op, src1, src2, dest, br_ok, br_fail)
+ print ("instr %d: (%d, %d, %d, %d, %d, %d)" % \
+ (i, src1, src2, dest, op, opi, imm))
+ alusim.op(op, opi, imm, src1, src2, dest)
+ yield from instr_q(dut, op, opi, imm, src1, src2, dest,
+ br_ok, br_fail)
# wait for all instructions to stop before checking
+ while True:
+ iqlen = yield dut.qlen_o
+ if iqlen == 0:
+ break
+ yield
+ yield
+ yield
+ yield
yield
yield from wait_for_busy_clear(dut)
def test_scoreboard():
- dut = Scoreboard(16, 8)
+ dut = IssueToScoreboard(2, 1, 1, 16, 8, 8)
alusim = RegSim(16, 8)
+ memsim = MemSim(16, 16)
vl = rtlil.convert(dut, ports=dut.ports())
with open("test_scoreboard6600.il", "w") as f:
f.write(vl)