From d3b64e32129089e714aa5e440ab24bfde364ad92 Mon Sep 17 00:00:00 2001 From: Luke Kenneth Casson Leighton Date: Thu, 30 May 2019 22:19:40 +0100 Subject: [PATCH] add instruction queue test --- src/scoreboard/instruction_q.py | 90 ++++++++++++++++---------- src/scoreboard/test_iq.py | 111 ++++++++++++++++++++++++++++++++ 2 files changed, 167 insertions(+), 34 deletions(-) create mode 100644 src/scoreboard/test_iq.py diff --git a/src/scoreboard/instruction_q.py b/src/scoreboard/instruction_q.py index ae513758..f3be7669 100644 --- a/src/scoreboard/instruction_q.py +++ b/src/scoreboard/instruction_q.py @@ -1,3 +1,5 @@ +from math import log + from nmigen.compat.sim import run_simulation from nmigen.cli import verilog, rtlil from nmigen import Module, Signal, Cat, Array, Const, Repl, Elaboratable @@ -24,9 +26,11 @@ class Instruction(RecordObject): class InstructionQ(Elaboratable): """ contains a queue of (part-decoded) instructions. - it is expected that the user of this queue will simply - inspect the queue contents directly, indicating at the start - of each clock cycle how many need to be removed. + output is copied combinatorially from the front of the queue, + for easy access on the clock cycle. only "n_in" instructions + are made available this way + + input and shifting occurs on sync. """ def __init__(self, wid, opwid, iqlen, n_in, n_out): """ constructor @@ -43,21 +47,22 @@ class InstructionQ(Elaboratable): self.opwid = opwid self.n_in = n_in self.n_out = n_out + mqbits = (int(log(iqlen) / log(2))+2, False) - self.p_add_i = Signal(max=n_in) # instructions to add (from data_i) + 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", wid, opwid) self.data_o = Instruction.nq(n_out, "data_o", wid, opwid) - self.n_sub_i = Signal(max=n_out) # number of instructions to remove - self.n_sub_o = Signal(max=n_out) # number of instructions removed + self.n_sub_i = Signal(mqbits) # number of instructions to remove + self.n_sub_o = Signal(mqbits) # number of instructions removed self.qsz = shape(self.data_o[0])[0] q = [] for i in range(iqlen): q.append(Signal(self.qsz, name="q%d" % i)) self.q = Array(q) - self.qlen_o = Signal(max=iqlen) + self.qlen_o = Signal(mqbits) def elaborate(self, platform): m = Module() @@ -65,41 +70,58 @@ class InstructionQ(Elaboratable): sync = m.d.sync iqlen = self.iqlen - mqlen = Const(iqlen, iqlen+1) + mqbits = int(log(iqlen) / log(2)) - start_copy = Signal(max=iqlen*2) - end_copy = Signal(max=iqlen*2) + left = Signal((mqbits+2, False)) + spare = Signal((mqbits+2, False)) + qmaxed = Signal() - # work out how many can be subtracted from the queue - with m.If(self.n_sub_i >= self.qlen_o): - comb += self.n_sub_o.eq(self.qlen_o) - with m.Elif(self.n_sub_i): - comb += self.n_sub_o.eq(self.n_sub_i) + start_q = Signal(mqbits) + end_q = Signal(mqbits) + mqlen = Const(iqlen, (len(left), False)) + print ("mqlen", mqlen) - # work out the start and end of where data can be written - comb += start_copy.eq(self.qlen_o - self.n_sub_o) - comb += end_copy.eq(start_copy + self.p_add_i) - comb += self.p_ready_o.eq((end_copy < self.qlen_o) & self.p_add_i) + # work out how many can be subtracted from the queue + with m.If(self.n_sub_i): + qinmax = Signal() + comb += qinmax.eq(self.n_sub_i > self.qlen_o) + with m.If(qinmax): + comb += self.n_sub_o.eq(self.qlen_o) + with m.Else(): + comb += self.n_sub_o.eq(self.n_sub_i) + + # work out how many new items are going to be in the queue + comb += left.eq(self.qlen_o - self.n_sub_o) + comb += spare.eq(mqlen - self.p_add_i) + comb += qmaxed.eq(left < spare) + comb += self.p_ready_o.eq(qmaxed & (self.p_add_i != 0)) # put q (flattened) into output for i in range(self.n_out): - comb += cat(self.data_o[i]).eq(self.q[i]) + opos = Signal(mqbits) + comb += opos.eq(end_q + i - self.n_out) # end hasn't moved yet + comb += cat(self.data_o[i]).eq(self.q[opos]) + + with m.If(self.n_sub_o): + # ok now the end's moved + sync += end_q.eq(end_q + self.n_sub_o) + + with m.If(self.p_ready_o): + # copy in the input... insanely gate-costly... *sigh*... + for i in range(self.n_in): + with m.If(self.p_add_i > Const(i, len(self.p_add_i))): + ipos = Signal(mqbits) + comb += ipos.eq(start_q + i) # should roll round + sync += self.q[ipos].eq(cat(self.data_i[i])) + sync += start_q.eq(start_q + self.p_add_i) - # this is going to be _so_ expensive in terms of gates... *sigh*... with m.If(self.p_ready_o): - for i in range(iqlen-1): - cfrom = Signal(max=iqlen*2) - cto = Signal(max=iqlen*2) - comb += cfrom.eq(Const(i, iqlen+1) + start_copy) - comb += cto.eq(Const(i, iqlen+1) + end_copy) - with m.If((cfrom < mqlen) & (cto < mqlen)): - sync += self.q[cto].eq(self.q[cfrom]) - - for i in range(self.n_in): - with m.If(self.p_add_i < i): - idx = Signal(max=iqlen) - comb += idx.eq(start_copy + i) - sync += self.q[idx].eq(cat(self.data_i[i])) + # update the queue length + add2 = Signal(mqbits+1) + comb += add2.eq(self.qlen_o + self.p_add_i) + sync += self.qlen_o.eq(add2 - self.n_sub_o) + with m.Else(): + sync += self.qlen_o.eq(self.qlen_o - self.n_sub_o) return m diff --git a/src/scoreboard/test_iq.py b/src/scoreboard/test_iq.py new file mode 100644 index 00000000..6bbb26db --- /dev/null +++ b/src/scoreboard/test_iq.py @@ -0,0 +1,111 @@ +""" testing of InstructionQ +""" + +from copy import deepcopy +from random import randint +from nmigen.compat.sim import run_simulation +from nmigen.cli import verilog, rtlil + +from scoreboard.instruction_q import InstructionQ +from nmutil.nmoperator import eq + + +class IQSim: + def __init__(self, dut, iq, n_in, n_out): + self.dut = dut + self.iq = iq + self.oq = [] + self.n_in = n_in + self.n_out = n_out + + def send(self): + i = 0 + while i < len(self.iq): + sendlen = randint(1, self.n_in) + print (sendlen) + sendlen = min(len(self.iq) - i, sendlen) + print (len(self.iq)-i, sendlen) + for idx in range(sendlen): + instr = self.iq[i+idx] + yield from eq(self.dut.data_i[idx], instr) + yield self.dut.p_add_i.eq(sendlen) + o_p_ready = yield self.dut.p_ready_o + while not o_p_ready: + yield + o_p_ready = yield self.dut.p_ready_o + + yield + + print ("send", len(self.iq), i, sendlen) + + yield self.dut.p_add_i.eq(0) + # wait random period of time before queueing another value + for j in range(randint(0, 3)): + yield + + i += sendlen + + yield self.dut.p_add_i.eq(0) + yield + + print ("send ended") + + ## wait random period of time before queueing another value + #for i in range(randint(0, 3)): + # yield + + #send_range = randint(0, 3) + #if send_range == 0: + # send = True + #else: + # send = randint(0, send_range) != 0 + + def rcv(self): + i = 0 + while i < len(self.iq): + rcvlen = randint(1, self.n_out) + #print ("outreq", rcvlen) + yield self.dut.n_sub_i.eq(rcvlen) + n_sub_o = yield self.dut.n_sub_o + yield + if n_sub_o == 0: + continue + + print ("recv", n_sub_o) + i += n_sub_o + + print ("recv ended") + + +def mk_insns(n_insns, wid, opwid): + res = [] + for i in range(n_insns): + op1 = randint(0, (1<