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