do instruction q as array of (flat) Signals, add in and out data
[soc.git] / src / scoreboard / instruction_q.py
1 from nmigen.compat.sim import run_simulation
2 from nmigen.cli import verilog, rtlil
3 from nmigen import Module, Signal, Cat, Array, Const, Repl, Elaboratable
4 from nmutil.iocontrol import RecordObject
5 from nmutil.nmoperator import eq, shape, cat
6
7
8 class Instruction(RecordObject):
9 def __init__(self, name, wid, opwid):
10 RecordObject.__init__(self, name=name)
11 self.oper_i = Signal(opwid, reset_less=True)
12 self.dest_i = Signal(wid, reset_less=True)
13 self.src1_i = Signal(wid, reset_less=True)
14 self.src2_i = Signal(wid, reset_less=True)
15
16 @staticmethod
17 def nq(n_insns, name, wid, opwid):
18 q = []
19 for i in range(n_insns):
20 q.append(Instruction("%s%d" % (name, i), wid, opwid))
21 return Array(q)
22
23
24 class InstructionQ(Elaboratable):
25 """ contains a queue of (part-decoded) instructions.
26
27 it is expected that the user of this queue will simply
28 inspect the queue contents directly, indicating at the start
29 of each clock cycle how many need to be removed.
30 """
31 def __init__(self, wid, opwid, iqlen, n_in, n_out):
32 """ constructor
33
34 Inputs
35
36 * :wid: register file width
37 * :opwid: operand width
38 * :iqlen: instruction queue length
39 * :n_in: max number of instructions allowed "in"
40 """
41 self.iqlen = iqlen
42 self.reg_width = wid
43 self.opwid = opwid
44 self.n_in = n_in
45 self.n_out = n_out
46
47 self.p_add_i = Signal(max=n_in) # instructions to add (from data_i)
48 self.p_ready_o = Signal() # instructions were added
49 self.data_i = Instruction.nq(n_in, "data_i", wid, opwid)
50
51 self.data_o = Instruction.nq(n_out, "data_o", wid, opwid)
52 self.n_sub_i = Signal(max=n_out) # number of instructions to remove
53 self.n_sub_o = Signal(max=n_out) # number of instructions removed
54
55 self.qsz = shape(self.data_o[0])[0]
56 q = []
57 for i in range(iqlen):
58 q.append(Signal(self.qsz, name="q%d" % i))
59 self.q = Array(q)
60 self.qlen_o = Signal(max=iqlen)
61
62 def elaborate(self, platform):
63 m = Module()
64 comb = m.d.comb
65 sync = m.d.sync
66
67 iqlen = self.iqlen
68 mqlen = Const(iqlen, iqlen+1)
69
70 start_copy = Signal(max=iqlen*2)
71 end_copy = Signal(max=iqlen*2)
72
73 # work out how many can be subtracted from the queue
74 with m.If(self.n_sub_i >= self.qlen_o):
75 comb += self.n_sub_o.eq(self.qlen_o)
76 with m.Elif(self.n_sub_i):
77 comb += self.n_sub_o.eq(self.n_sub_i)
78
79 # work out the start and end of where data can be written
80 comb += start_copy.eq(self.qlen_o - self.n_sub_o)
81 comb += end_copy.eq(start_copy + self.p_add_i)
82 comb += self.p_ready_o.eq((end_copy < self.qlen_o) & self.p_add_i)
83
84 # put q (flattened) into output
85 for i in range(self.n_out):
86 comb += cat(self.data_o[i]).eq(self.q[i])
87
88 # this is going to be _so_ expensive in terms of gates... *sigh*...
89 with m.If(self.p_ready_o):
90 for i in range(iqlen-1):
91 cfrom = Signal(max=iqlen*2)
92 cto = Signal(max=iqlen*2)
93 comb += cfrom.eq(Const(i, iqlen+1) + start_copy)
94 comb += cto.eq(Const(i, iqlen+1) + end_copy)
95 with m.If((cfrom < mqlen) & (cto < mqlen)):
96 sync += self.q[cto].eq(self.q[cfrom])
97
98 for i in range(self.n_in):
99 with m.If(self.p_add_i < i):
100 idx = Signal(max=iqlen)
101 comb += idx.eq(start_copy + i)
102 sync += self.q[idx].eq(cat(self.data_i[i]))
103
104 return m
105
106 def __iter__(self):
107 yield from self.q
108
109 yield self.p_ready_o
110 for o in self.data_i:
111 yield from list(o)
112 yield self.p_add_i
113
114 for o in self.data_o:
115 yield from list(o)
116 yield self.n_sub_i
117 yield self.n_sub_o
118
119 def ports(self):
120 return list(self)
121
122
123 def instruction_q_sim(dut):
124 yield dut.dest_i.eq(1)
125 yield dut.issue_i.eq(1)
126 yield
127 yield dut.issue_i.eq(0)
128 yield
129 yield dut.src1_i.eq(1)
130 yield dut.issue_i.eq(1)
131 yield
132 yield
133 yield
134 yield dut.issue_i.eq(0)
135 yield
136 yield dut.go_rd_i.eq(1)
137 yield
138 yield dut.go_rd_i.eq(0)
139 yield
140 yield dut.go_wr_i.eq(1)
141 yield
142 yield dut.go_wr_i.eq(0)
143 yield
144
145 def test_instruction_q():
146 dut = InstructionQ(16, 4, 4, n_in=2, n_out=2)
147 vl = rtlil.convert(dut, ports=dut.ports())
148 with open("test_instruction_q.il", "w") as f:
149 f.write(vl)
150
151 run_simulation(dut, instruction_q_sim(dut),
152 vcd_name='test_instruction_q.vcd')
153
154 if __name__ == '__main__':
155 test_instruction_q()