add name "test_issuer" to ilang conversion
[soc.git] / src / soc / simple / issuer.py
1 """simple core issuer
2
3 not in any way intended for production use. this runs a FSM that:
4
5 * reads the Program Counter from FastRegs
6 * reads an instruction from a fixed-size Test Memory
7 * issues it to the Simple Core
8 * waits for it to complete
9 * increments the PC
10 * does it all over again
11
12 the purpose of this module is to verify the functional correctness
13 of the Function Units in the absolute simplest and clearest possible
14 way, and to at provide something that can be further incrementally
15 improved.
16 """
17
18 from nmigen import Elaboratable, Module, Signal
19 from nmigen.cli import rtlil
20
21 from soc.decoder.decode2execute1 import Data
22 from soc.experiment.testmem import TestMemory # test only for instructions
23 from soc.regfile.regfiles import FastRegs
24 from soc.simple.core import NonProductionCore
25 from soc.config.test.test_loadstore import TestMemPspec
26 from soc.config.ifetch import ConfigFetchUnit
27
28
29 class TestIssuer(Elaboratable):
30 """TestIssuer - reads instructions from TestMemory and issues them
31
32 efficiency and speed is not the main goal here: functional correctness is.
33 """
34 def __init__(self, addrwid=6, idepth=6, ifacetype='testpi',
35 imemtype='testmem'):
36 # main instruction core
37 self.core = core = NonProductionCore(addrwid, ifacetype=ifacetype)
38
39 pspec = TestMemPspec(ldst_ifacetype=ifacetype,
40 imem_ifacetype=imemtype,
41 addr_wid=addrwid<<1,
42 mask_wid=8,
43 reg_wid=64) # instruction memory width
44 # Test Instruction memory
45 self.imem = ConfigFetchUnit(pspec).fu
46 # one-row cache of instruction read
47 self.iline = Signal(64) # one instruction line
48 self.iprev_adr = Signal(64) # previous address: if different, do read
49
50 # instruction go/monitor
51 self.go_insn_i = Signal(reset_less=True)
52 self.pc_o = Signal(64, reset_less=True)
53 self.pc_i = Data(64, "pc") # set "ok" to indicate "please change me"
54 self.busy_o = core.busy_o
55 self.memerr_o = Signal(reset_less=True)
56
57 # FAST regfile read /write ports
58 self.fast_rd1 = self.core.regs.rf['fast'].r_ports['d_rd1']
59 self.fast_wr1 = self.core.regs.rf['fast'].w_ports['d_wr1']
60 # hack method of keeping an eye on whether branch/trap set the PC
61 self.fast_nia = self.core.regs.rf['fast'].w_ports['nia']
62 self.fast_nia.wen.name = 'fast_nia_wen'
63
64 def elaborate(self, platform):
65 m = Module()
66 comb, sync = m.d.comb, m.d.sync
67
68 m.submodules.core = core = self.core
69 m.submodules.imem = imem = self.imem
70
71 # temporary hack: says "go" immediately for both address gen and ST
72 l0 = core.l0
73 ldst = core.fus.fus['ldst0']
74 m.d.comb += ldst.ad.go.eq(ldst.ad.rel) # link addr-go direct to rel
75 m.d.comb += ldst.st.go.eq(ldst.st.rel) # link store-go direct to rel
76
77 # PC and instruction from I-Memory
78 current_insn = Signal(32) # current fetched instruction (note sync)
79 current_pc = Signal(64) # current PC (note it is reset/sync)
80 pc_changed = Signal() # note write to PC
81 comb += self.pc_o.eq(current_pc)
82 ilatch = Signal(32)
83
84 # next instruction (+4 on current)
85 nia = Signal(64, reset_less=True)
86 comb += nia.eq(current_pc + 4)
87
88 # temporaries
89 core_busy_o = core.busy_o # core is busy
90 core_ivalid_i = core.ivalid_i # instruction is valid
91 core_issue_i = core.issue_i # instruction is issued
92 core_be_i = core.bigendian_i # bigendian mode
93 core_opcode_i = core.raw_opcode_i # raw opcode
94
95 # actually use a nmigen FSM for the first time (w00t)
96 with m.FSM() as fsm:
97
98 # waiting (zzz)
99 with m.State("IDLE"):
100 sync += pc_changed.eq(0)
101 with m.If(self.go_insn_i):
102 # instruction allowed to go: start by reading the PC
103 pc = Signal(64, reset_less=True)
104 with m.If(self.pc_i.ok):
105 # incoming override (start from pc_i)
106 comb += pc.eq(self.pc_i.data)
107 with m.Else():
108 # otherwise read FastRegs regfile for PC
109 comb += self.fast_rd1.ren.eq(1<<FastRegs.PC)
110 comb += pc.eq(self.fast_rd1.data_o)
111 # capture the PC and also drop it into Insn Memory
112 # we have joined a pair of combinatorial memory
113 # lookups together. this is Generally Bad.
114 comb += self.imem.a_pc_i.eq(pc)
115 comb += self.imem.a_valid_i.eq(1)
116 comb += self.imem.f_valid_i.eq(1)
117 sync += current_pc.eq(pc)
118 m.next = "INSN_READ" # move to "wait for bus" phase
119
120 # waiting for instruction bus (stays there until not busy)
121 with m.State("INSN_READ"):
122 with m.If(self.imem.f_busy_o): # zzz...
123 # busy: stay in wait-read
124 comb += self.imem.a_valid_i.eq(1)
125 comb += self.imem.f_valid_i.eq(1)
126 with m.Else():
127 # not busy: instruction fetched
128 insn = self.imem.f_instr_o.word_select(current_pc[2], 32)
129 comb += current_insn.eq(insn)
130 comb += core_ivalid_i.eq(1) # say instruction is valid
131 comb += core_issue_i.eq(1) # and issued (ivalid redundant)
132 comb += core_be_i.eq(0) # little-endian mode
133 comb += core_opcode_i.eq(current_insn) # actual opcode
134 sync += ilatch.eq(current_insn)
135 m.next = "INSN_ACTIVE" # move to "wait for completion" phase
136
137 # instruction started: must wait till it finishes
138 with m.State("INSN_ACTIVE"):
139 comb += core_ivalid_i.eq(1) # say instruction is valid
140 comb += core_opcode_i.eq(ilatch) # actual opcode
141 #sync += core_issue_i.eq(0) # issue raises for only one cycle
142 with m.If(self.fast_nia.wen):
143 sync += pc_changed.eq(1)
144 with m.If(~core_busy_o): # instruction done!
145 #sync += core_ivalid_i.eq(0) # say instruction is invalid
146 #sync += core_opcode_i.eq(0) # clear out (no good reason)
147 # ok here we are not reading the branch unit. TODO
148 # this just blithely overwrites whatever pipeline updated
149 # the PC
150 with m.If(~pc_changed):
151 comb += self.fast_wr1.wen.eq(1<<FastRegs.PC)
152 comb += self.fast_wr1.data_i.eq(nia)
153 m.next = "IDLE" # back to idle
154
155 return m
156
157 def __iter__(self):
158 yield from self.pc_i.ports()
159 yield self.pc_o
160 yield self.go_insn_i
161 yield self.memerr_o
162 yield from self.core.ports()
163 yield from self.imem.ports()
164
165 def ports(self):
166 return list(self)
167
168
169 if __name__ == '__main__':
170 dut = TestIssuer()
171 vl = rtlil.convert(dut, ports=dut.ports(), name="test_issuer")
172 with open("test_issuer.il", "w") as f:
173 f.write(vl)
174