leave off number being subtracted from "ready_o" calculation
[soc.git] / src / scoreboard / instruction_q.py
1 from math import log
2
3 from nmigen.compat.sim import run_simulation
4 from nmigen.cli import verilog, rtlil
5 from nmigen import Module, Signal, Cat, Array, Const, Repl, Elaboratable
6 from nmutil.iocontrol import RecordObject
7 from nmutil.nmoperator import eq, shape, cat
8
9
10 class Instruction(RecordObject):
11 def __init__(self, name, wid, opwid):
12 RecordObject.__init__(self, name=name)
13 self.oper_i = Signal(opwid, reset_less=True)
14 self.dest_i = Signal(wid, reset_less=True)
15 self.src1_i = Signal(wid, reset_less=True)
16 self.src2_i = Signal(wid, reset_less=True)
17
18 @staticmethod
19 def nq(n_insns, name, wid, opwid):
20 q = []
21 for i in range(n_insns):
22 q.append(Instruction("%s%d" % (name, i), wid, opwid))
23 return Array(q)
24
25
26 class InstructionQ(Elaboratable):
27 """ contains a queue of (part-decoded) instructions.
28
29 output is copied combinatorially from the front of the queue,
30 for easy access on the clock cycle. only "n_in" instructions
31 are made available this way
32
33 input and shifting occurs on sync.
34 """
35 def __init__(self, wid, opwid, iqlen, n_in, n_out):
36 """ constructor
37
38 Inputs
39
40 * :wid: register file width
41 * :opwid: operand width
42 * :iqlen: instruction queue length
43 * :n_in: max number of instructions allowed "in"
44 """
45 self.iqlen = iqlen
46 self.reg_width = wid
47 self.opwid = opwid
48 self.n_in = n_in
49 self.n_out = n_out
50 mqbits = (int(log(iqlen) / log(2))+2, False)
51
52 self.p_add_i = Signal(mqbits) # instructions to add (from data_i)
53 self.p_ready_o = Signal() # instructions were added
54 self.data_i = Instruction.nq(n_in, "data_i", wid, opwid)
55
56 self.data_o = Instruction.nq(n_out, "data_o", wid, opwid)
57 self.n_sub_i = Signal(mqbits) # number of instructions to remove
58 self.n_sub_o = Signal(mqbits) # number of instructions removed
59
60 self.qsz = shape(self.data_o[0])[0]
61 q = []
62 for i in range(iqlen):
63 q.append(Signal(self.qsz, name="q%d" % i))
64 self.q = Array(q)
65 self.qlen_o = Signal(mqbits)
66
67 def elaborate(self, platform):
68 m = Module()
69 comb = m.d.comb
70 sync = m.d.sync
71
72 iqlen = self.iqlen
73 mqbits = int(log(iqlen) / log(2))
74
75 left = Signal((mqbits+2, False))
76 spare = Signal((mqbits+2, False))
77 qmaxed = Signal()
78
79 start_q = Signal(mqbits)
80 end_q = Signal(mqbits)
81 mqlen = Const(iqlen, (len(left), False))
82 print ("mqlen", mqlen)
83
84 # work out how many can be subtracted from the queue
85 with m.If(self.n_sub_i):
86 qinmax = Signal()
87 comb += qinmax.eq(self.n_sub_i > self.qlen_o)
88 with m.If(qinmax):
89 comb += self.n_sub_o.eq(self.qlen_o)
90 with m.Else():
91 comb += self.n_sub_o.eq(self.n_sub_i)
92
93 # work out how many new items are going to be in the queue
94 comb += left.eq(self.qlen_o )#- self.n_sub_o)
95 comb += spare.eq(mqlen - self.p_add_i)
96 comb += qmaxed.eq(left <= spare)
97 comb += self.p_ready_o.eq(qmaxed & (self.p_add_i != 0))
98
99 # put q (flattened) into output
100 for i in range(self.n_out):
101 opos = Signal(mqbits)
102 comb += opos.eq(end_q + i - self.n_out) # end hasn't moved yet
103 comb += cat(self.data_o[i]).eq(self.q[opos])
104
105 with m.If(self.n_sub_o):
106 # ok now the end's moved
107 sync += end_q.eq(end_q + self.n_sub_o)
108
109 with m.If(self.p_ready_o):
110 # copy in the input... insanely gate-costly... *sigh*...
111 for i in range(self.n_in):
112 with m.If(self.p_add_i > Const(i, len(self.p_add_i))):
113 ipos = Signal(mqbits)
114 comb += ipos.eq(start_q + i) # should roll round
115 sync += self.q[ipos].eq(cat(self.data_i[i]))
116 sync += start_q.eq(start_q + self.p_add_i)
117
118 with m.If(self.p_ready_o):
119 # update the queue length
120 add2 = Signal(mqbits+1)
121 comb += add2.eq(self.qlen_o + self.p_add_i)
122 sync += self.qlen_o.eq(add2 - self.n_sub_o)
123 with m.Else():
124 sync += self.qlen_o.eq(self.qlen_o - self.n_sub_o)
125
126 return m
127
128 def __iter__(self):
129 yield from self.q
130
131 yield self.p_ready_o
132 for o in self.data_i:
133 yield from list(o)
134 yield self.p_add_i
135
136 for o in self.data_o:
137 yield from list(o)
138 yield self.n_sub_i
139 yield self.n_sub_o
140
141 def ports(self):
142 return list(self)
143
144
145 def instruction_q_sim(dut):
146 yield dut.dest_i.eq(1)
147 yield dut.issue_i.eq(1)
148 yield
149 yield dut.issue_i.eq(0)
150 yield
151 yield dut.src1_i.eq(1)
152 yield dut.issue_i.eq(1)
153 yield
154 yield
155 yield
156 yield dut.issue_i.eq(0)
157 yield
158 yield dut.go_rd_i.eq(1)
159 yield
160 yield dut.go_rd_i.eq(0)
161 yield
162 yield dut.go_wr_i.eq(1)
163 yield
164 yield dut.go_wr_i.eq(0)
165 yield
166
167 def test_instruction_q():
168 dut = InstructionQ(16, 4, 4, n_in=2, n_out=2)
169 vl = rtlil.convert(dut, ports=dut.ports())
170 with open("test_instruction_q.il", "w") as f:
171 f.write(vl)
172
173 run_simulation(dut, instruction_q_sim(dut),
174 vcd_name='test_instruction_q.vcd')
175
176 if __name__ == '__main__':
177 test_instruction_q()