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