55b2b83d81cd5a8540b60ef98faf68eca43ce2d4
[soc.git] / src / experiment / compldst.py
1 from nmigen.compat.sim import run_simulation
2 from nmigen.cli import verilog, rtlil
3 from nmigen import Module, Signal, Elaboratable
4
5 from nmutil.latch import SRLatch, latchregister
6
7 """ LOAD / STORE Computation Unit. Also capable of doing ADD and ADD immediate
8
9 This module runs a "revolving door" set of four latches, based on
10 * Issue
11 * Go_Read
12 * Go_Addr
13 * Go_Write *OR* Go_Store
14
15 (Note that opc_l has been inverted (and qn used), due to SRLatch
16 default reset state being "0" rather than "1")
17 """
18
19 # internal opcodes. hypothetically this could do more combinations.
20 # meanings:
21 # * bit 0: 0 = ADD , 1 = SUB
22 # * bit 1: 0 = src1, 1 = IMM
23 # * bit 2: 1 = LD
24 # * bit 3: 1 = ST
25 LDST_OP_ADDI = 0b0000 # plain ADD (src1 + src2)
26 LDST_OP_SUBI = 0b0001 # plain SUB (src1 - src2)
27 LDST_OP_ADD = 0b0010 # immed ADD (imm + src1)
28 LDST_OP_SUB = 0b0011 # immed SUB (imm - src1)
29 LDST_OP_ST = 0b0110 # immed ADD plus LD op. ADD result is address
30 LDST_OP_LD = 0b1010 # immed ADD plus ST op. ADD result is address
31
32
33 class LDSTCompUnit(Elaboratable):
34 def __init__(self, rwid, opwid, alu, mem):
35 self.rwid = rwid
36 self.alu = alu
37 self.mem = mem
38
39 self.counter = Signal(4)
40 self.go_rd_i = Signal(reset_less=True) # go read in
41 self.go_ad_i = Signal(reset_less=True) # go address in
42 self.go_wr_i = Signal(reset_less=True) # go write in
43 self.go_st_i = Signal(reset_less=True) # go store in
44 self.issue_i = Signal(reset_less=True) # fn issue in
45 self.isalu_i = Signal(reset_less=True) # fn issue as ALU in
46 self.shadown_i = Signal(reset=1) # shadow function, defaults to ON
47 self.go_die_i = Signal() # go die (reset)
48
49 self.oper_i = Signal(opwid, reset_less=True) # opcode in
50 self.imm_i = Signal(rwid, reset_less=True) # immediate in
51 self.src1_i = Signal(rwid, reset_less=True) # oper1 in
52 self.src2_i = Signal(rwid, reset_less=True) # oper2 in
53
54 self.busy_o = Signal(reset_less=True) # fn busy out
55 self.rd_rel_o = Signal(reset_less=True) # request src1/src2
56 self.adr_rel_o = Signal(reset_less=True) # request address (from mem)
57 self.sto_rel_o = Signal(reset_less=True) # request store (to mem)
58 self.req_rel_o = Signal(reset_less=True) # request write (result)
59 self.data_o = Signal(rwid, reset_less=True) # Dest out (LD or ALU)
60 self.load_mem_o = Signal(reset_less=True) # activate memory LOAD
61 self.stwd_mem_o = Signal(reset_less=True) # activate memory STORE
62
63 def elaborate(self, platform):
64 m = Module()
65 m.submodules.alu = self.alu
66 m.submodules.src_l = src_l = SRLatch(sync=False)
67 m.submodules.opc_l = opc_l = SRLatch(sync=False)
68 m.submodules.adr_l = adr_l = SRLatch(sync=False)
69 m.submodules.req_l = req_l = SRLatch(sync=False)
70 m.submodules.sto_l = sto_l = SRLatch(sync=False)
71
72 # shadow/go_die
73 reset_w = Signal(reset_less=True)
74 reset_a = Signal(reset_less=True)
75 reset_s = Signal(reset_less=True)
76 reset_r = Signal(reset_less=True)
77 m.d.comb += reset_b.eq(self.go_st_ | self.go_wr_i | self.go_die_i)
78 m.d.comb += reset_w.eq(self.go_wr_i | self.go_die_i)
79 m.d.comb += reset_s.eq(self.go_st_i | self.go_die_i)
80 m.d.comb += reset_r.eq(self.go_rd_i | self.go_die_i)
81 # this one is slightly different, issue_alu_i selects go_wr_i)
82 a_sel = Mux(self.isalu_i, self.go_wr_i, self.go_ad_i )
83 m.d.comb += reset_a.eq(a_sel| self.go_die_i)
84
85 # opcode decode
86 op_alu = Signal(reset_less=True)
87 op_is_ld = Signal(reset_less=True)
88 op_is_st = Signal(reset_less=True)
89 op_is_imm = Signal(reset_less=True)
90 m.d.comb += op_alu.eq(self.oper_i[0])
91 m.d.comb += op_is_imm.eq(self.oper_i[1])
92 m.d.comb += op_is_ld.eq(self.oper_i[2])
93 m.d.comb += op_is_st.eq(self.oper_i[3])
94 m.d.comb += self.load_mem_o.eq(op_is_ld & self.go_ad_i)
95 m.d.comb += self.stwd_mem_o.eq(op_is_st & self.go_st_i)
96
97 # select immediate or src2 reg to add
98 src2_or_imm = Signal(self.rwid, reset_less=True)
99 src_sel = Signal(reset_less=True)
100
101 # issue can be either issue_i or issue_alu_i (isalu_i)
102 issue_i = Signal(reset_less=True)
103 m.d.comb += issue_i.eq(self.issue_i | self.isalu_i)
104
105 # Ripple-down the latches, each one set cancels the previous.
106 # NOTE: use sync to stop combinatorial loops.
107
108 # opcode latch - inverted so that busy resets to 0
109 m.d.sync += opc_l.s.eq(issue_i) # XXX NOTE: INVERTED FROM book!
110 m.d.sync += opc_l.r.eq(reset_b) # XXX NOTE: INVERTED FROM book!
111
112 # src operand latch
113 m.d.sync += src_l.s.eq(issue_i)
114 m.d.sync += src_l.r.eq(reset_r)
115
116 # addr latch
117 m.d.sync += req_l.s.eq(self.go_rd_i)
118 m.d.sync += req_l.r.eq(reset_a)
119
120 # dest operand latch
121 m.d.sync += req_l.s.eq(self.go_ad_i)
122 m.d.sync += req_l.r.eq(reset_w)
123
124 # dest operand latch
125 m.d.sync += req_l.s.eq(self.go_ad_i)
126 m.d.sync += req_l.r.eq(reset_s)
127
128 # outputs
129 m.d.comb += self.busy_o.eq(opc_l.q) # busy out
130 m.d.comb += self.rd_rel_o.eq(src_l.q & opc_l.q) # src1/src2 req rel
131
132 # the counter is just for demo purposes, to get the ALUs of different
133 # types to take arbitrary completion times
134 with m.If(opc_l.qn):
135 m.d.sync += self.counter.eq(0)
136 with m.If(req_l.qn & opc_l.q & (self.counter == 0)):
137 with m.If(self.oper_i == 2): # MUL, to take 5 instructions
138 m.d.sync += self.counter.eq(5)
139 with m.Elif(self.oper_i == 3): # SHIFT to take 7
140 m.d.sync += self.counter.eq(7)
141 with m.Else(): # ADD/SUB to take 2
142 m.d.sync += self.counter.eq(2)
143 with m.If(self.counter > 1):
144 m.d.sync += self.counter.eq(self.counter - 1)
145 with m.If(self.counter == 1):
146 # write req release out. waits until shadow is dropped.
147 m.d.comb += self.req_rel_o.eq(req_l.q & opc_l.q & self.shadown_i)
148
149 # select immediate if opcode says so. however also change the latch
150 # to trigger *from* the opcode latch instead.
151 m.d.comb += src_sel.eq(Mux(op_is_imm, opc_l.q, src_l.q))
152 m.d.comb += src2_or_imm.eq(Mux(op_is_imm, self.imm_i, self.src2_i)
153
154 # create a latch/register for src1/src2 (include immediate select)
155 latchregister(m, self.src1_i, self.alu.a, src_l.q)
156 latchregister(m, src2_or_imm, self.alu.b, src_sel)
157
158 # create a latch/register for the operand
159 latchregister(m, self.oper_i, self.alu.op, self.issue_i)
160
161 # and one for the output from the ALU
162 data_r = Signal(self.rwid, reset_less=True) # Dest register
163 latchregister(m, self.alu.o, data_r, req_l.q)
164
165 with m.If(self.go_wr_i):
166 m.d.comb += self.data_o.eq(data_r)
167
168 return m
169
170 def scoreboard_sim(dut):
171 yield dut.dest_i.eq(1)
172 yield dut.issue_i.eq(1)
173 yield
174 yield dut.issue_i.eq(0)
175 yield
176 yield dut.src1_i.eq(1)
177 yield dut.issue_i.eq(1)
178 yield
179 yield
180 yield
181 yield dut.issue_i.eq(0)
182 yield
183 yield dut.go_read_i.eq(1)
184 yield
185 yield dut.go_read_i.eq(0)
186 yield
187 yield dut.go_write_i.eq(1)
188 yield
189 yield dut.go_write_i.eq(0)
190 yield
191
192 def test_scoreboard():
193 dut = Scoreboard(32, 8)
194 vl = rtlil.convert(dut, ports=dut.ports())
195 with open("test_scoreboard.il", "w") as f:
196 f.write(vl)
197
198 run_simulation(dut, scoreboard_sim(dut), vcd_name='test_scoreboard.vcd')
199
200 if __name__ == '__main__':
201 test_scoreboard()