use single-arg pspec for TestIssuer and Core
[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, pspec):
35 # main instruction core
36 self.core = core = NonProductionCore(pspec)
37
38 # Test Instruction memory
39 self.imem = ConfigFetchUnit(pspec).fu
40 # one-row cache of instruction read
41 self.iline = Signal(64) # one instruction line
42 self.iprev_adr = Signal(64) # previous address: if different, do read
43
44 # instruction go/monitor
45 self.go_insn_i = Signal(reset_less=True)
46 self.pc_o = Signal(64, reset_less=True)
47 self.pc_i = Data(64, "pc") # set "ok" to indicate "please change me"
48 self.busy_o = core.busy_o
49 self.memerr_o = Signal(reset_less=True)
50
51 # FAST regfile read /write ports
52 self.fast_rd1 = self.core.regs.rf['fast'].r_ports['d_rd1']
53 self.fast_wr1 = self.core.regs.rf['fast'].w_ports['d_wr1']
54 # hack method of keeping an eye on whether branch/trap set the PC
55 self.fast_nia = self.core.regs.rf['fast'].w_ports['nia']
56 self.fast_nia.wen.name = 'fast_nia_wen'
57
58 def elaborate(self, platform):
59 m = Module()
60 comb, sync = m.d.comb, m.d.sync
61
62 m.submodules.core = core = self.core
63 m.submodules.imem = imem = self.imem
64
65 # temporary hack: says "go" immediately for both address gen and ST
66 l0 = core.l0
67 ldst = core.fus.fus['ldst0']
68 m.d.comb += ldst.ad.go.eq(ldst.ad.rel) # link addr-go direct to rel
69 m.d.comb += ldst.st.go.eq(ldst.st.rel) # link store-go direct to rel
70
71 # PC and instruction from I-Memory
72 current_insn = Signal(32) # current fetched instruction (note sync)
73 current_pc = Signal(64) # current PC (note it is reset/sync)
74 pc_changed = Signal() # note write to PC
75 comb += self.pc_o.eq(current_pc)
76 ilatch = Signal(32)
77
78 # next instruction (+4 on current)
79 nia = Signal(64, reset_less=True)
80 comb += nia.eq(current_pc + 4)
81
82 # temporaries
83 core_busy_o = core.busy_o # core is busy
84 core_ivalid_i = core.ivalid_i # instruction is valid
85 core_issue_i = core.issue_i # instruction is issued
86 core_be_i = core.bigendian_i # bigendian mode
87 core_opcode_i = core.raw_opcode_i # raw opcode
88
89 # actually use a nmigen FSM for the first time (w00t)
90 with m.FSM() as fsm:
91
92 # waiting (zzz)
93 with m.State("IDLE"):
94 sync += pc_changed.eq(0)
95 with m.If(self.go_insn_i):
96 # instruction allowed to go: start by reading the PC
97 pc = Signal(64, reset_less=True)
98 with m.If(self.pc_i.ok):
99 # incoming override (start from pc_i)
100 comb += pc.eq(self.pc_i.data)
101 with m.Else():
102 # otherwise read FastRegs regfile for PC
103 comb += self.fast_rd1.ren.eq(1<<FastRegs.PC)
104 comb += pc.eq(self.fast_rd1.data_o)
105 # capture the PC and also drop it into Insn Memory
106 # we have joined a pair of combinatorial memory
107 # lookups together. this is Generally Bad.
108 comb += self.imem.a_pc_i.eq(pc)
109 comb += self.imem.a_valid_i.eq(1)
110 comb += self.imem.f_valid_i.eq(1)
111 sync += current_pc.eq(pc)
112 m.next = "INSN_READ" # move to "wait for bus" phase
113
114 # waiting for instruction bus (stays there until not busy)
115 with m.State("INSN_READ"):
116 with m.If(self.imem.f_busy_o): # zzz...
117 # busy: stay in wait-read
118 comb += self.imem.a_valid_i.eq(1)
119 comb += self.imem.f_valid_i.eq(1)
120 with m.Else():
121 # not busy: instruction fetched
122 insn = self.imem.f_instr_o.word_select(current_pc[2], 32)
123 comb += current_insn.eq(insn)
124 comb += core_ivalid_i.eq(1) # say instruction is valid
125 comb += core_issue_i.eq(1) # and issued (ivalid redundant)
126 comb += core_be_i.eq(0) # little-endian mode
127 comb += core_opcode_i.eq(current_insn) # actual opcode
128 sync += ilatch.eq(current_insn)
129 m.next = "INSN_ACTIVE" # move to "wait for completion" phase
130
131 # instruction started: must wait till it finishes
132 with m.State("INSN_ACTIVE"):
133 comb += core_ivalid_i.eq(1) # say instruction is valid
134 comb += core_opcode_i.eq(ilatch) # actual opcode
135 #sync += core_issue_i.eq(0) # issue raises for only one cycle
136 with m.If(self.fast_nia.wen):
137 sync += pc_changed.eq(1)
138 with m.If(~core_busy_o): # instruction done!
139 #sync += core_ivalid_i.eq(0) # say instruction is invalid
140 #sync += core_opcode_i.eq(0) # clear out (no good reason)
141 # ok here we are not reading the branch unit. TODO
142 # this just blithely overwrites whatever pipeline updated
143 # the PC
144 with m.If(~pc_changed):
145 comb += self.fast_wr1.wen.eq(1<<FastRegs.PC)
146 comb += self.fast_wr1.data_i.eq(nia)
147 m.next = "IDLE" # back to idle
148
149 return m
150
151 def __iter__(self):
152 yield from self.pc_i.ports()
153 yield self.pc_o
154 yield self.go_insn_i
155 yield self.memerr_o
156 yield from self.core.ports()
157 yield from self.imem.ports()
158
159 def ports(self):
160 return list(self)
161
162
163 if __name__ == '__main__':
164 pspec = TestMemPspec(ldst_ifacetype='testpi',
165 imem_ifacetype='testmem',
166 addr_wid=48,
167 mask_wid=8,
168 reg_wid=64)
169 dut = TestIssuer(pspec)
170 vl = rtlil.convert(dut, ports=dut.ports(), name="test_issuer")
171 with open("test_issuer.il", "w") as f:
172 f.write(vl)
173