add start of instruction queue
[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
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.reg_width = wid
42 self.opwid = opwid
43 self.n_in = n_in
44 self.n_out = n_out
45
46 self.q = Instruction.nq(iqlen, "i", wid, opwid)
47 self.qlen_o = Signal(max=iqlen)
48
49 self.p_add_i = Signal(max=n_in) # instructions to add (from data_i)
50 self.p_ready_o = Signal() # instructions were added
51 self.data_i = Instruction.nq(n_in, "data_i", wid, opwid)
52
53 self.data_o = Instruction.nq(n_out, "data_o", wid, opwid)
54 self.n_sub_i = Signal(max=n_out) # number of instructions to remove
55 self.n_sub_o = Signal(max=n_out) # number of instructions removed
56
57 def elaborate(self, platform):
58 m = Module()
59 comb = m.d.comb
60 sync = m.d.sync
61
62 iqlen = len(self.q)
63 mqlen = Const(iqlen, iqlen*2)
64
65 start_copy = Signal(max=iqlen*2)
66 end_copy = Signal(max=iqlen*2)
67
68 # work out how many can be subtracted from the queue
69 with m.If(self.n_sub_i >= self.qlen_o):
70 comb += self.n_sub_o.eq(self.qlen_o)
71 with m.Elif(self.n_sub_i):
72 comb += self.n_sub_o.eq(self.n_sub_i)
73
74 # work out the start and end of where data can be written
75 comb += start_copy.eq(self.qlen_o - self.n_sub_o)
76 comb += end_copy.eq(start_copy + self.p_add_i - 1)
77 comb += self.p_ready_o.eq(end_copy < self.qlen_o) # ready if room exists
78
79 # this is going to be _so_ expensive in terms of gates... *sigh*...
80 with m.If(self.p_ready_o):
81 for i in range(iqlen):
82 cfrom = Signal(max=iqlen*2)
83 cto = Signal(max=iqlen*2)
84 comb += cfrom.eq(Const(i, iqlen+1) + start_copy)
85 comb += cto.eq(Const(i, iqlen+1) + end_copy)
86 with m.If((cfrom < mqlen) & (cto < mqlen)):
87 sync += self.q[cto].oper_i.eq(self.q[cfrom].oper_i)
88 sync += self.q[cto].dest_i.eq(self.q[cfrom].dest_i)
89 sync += self.q[cto].src1_i.eq(self.q[cfrom].src1_i)
90 sync += self.q[cto].src2_i.eq(self.q[cfrom].src2_i)
91
92 return m
93
94 def __iter__(self):
95 for o in self.q:
96 yield from list(o)
97 yield self.qlen_o
98
99 yield self.p_ready_o
100 for o in self.data_i:
101 yield from list(o)
102 yield self.p_add_i
103
104 for o in self.data_o:
105 yield from list(o)
106 yield self.n_sub_i
107 yield self.n_sub_o
108
109 def ports(self):
110 return list(self)
111
112
113 def instruction_q_sim(dut):
114 yield dut.dest_i.eq(1)
115 yield dut.issue_i.eq(1)
116 yield
117 yield dut.issue_i.eq(0)
118 yield
119 yield dut.src1_i.eq(1)
120 yield dut.issue_i.eq(1)
121 yield
122 yield
123 yield
124 yield dut.issue_i.eq(0)
125 yield
126 yield dut.go_rd_i.eq(1)
127 yield
128 yield dut.go_rd_i.eq(0)
129 yield
130 yield dut.go_wr_i.eq(1)
131 yield
132 yield dut.go_wr_i.eq(0)
133 yield
134
135 def test_instruction_q():
136 dut = InstructionQ(16, 4, 4, n_in=2, n_out=2)
137 vl = rtlil.convert(dut, ports=dut.ports())
138 with open("test_instruction_q.il", "w") as f:
139 f.write(vl)
140
141 run_simulation(dut, instruction_q_sim(dut),
142 vcd_name='test_instruction_q.vcd')
143
144 if __name__ == '__main__':
145 test_instruction_q()