only add pc_i in DMI mode
[libresoc-litex.git] / sim.py
1 #!/usr/bin/env python3
2
3 # Notes for "Debug" mode:
4 # both microwatt and Libre-SOC implement (pretty much) the same DMI
5 # interface. TBD: really, there should be an OPF Debug SIG which
6 # defines this properly. still, these two are interoperable.
7 # https://git.libre-soc.org/?p=soc.git;a=blob;f=src/soc/debug/dmi.py
8 # https://github.com/antonblanchard/microwatt/blob/master/core_debug.vhdl
9
10 import os
11 import argparse
12
13 from migen import (Signal, FSM, If, Display, Finish, NextValue, NextState)
14
15 from litex.build.generic_platform import Pins, Subsignal
16 from litex.build.sim import SimPlatform
17 from litex.build.io import CRG
18 from litex.build.sim.config import SimConfig
19
20 from litex.soc.integration.soc import SoCRegion
21 from litex.soc.integration.soc_core import SoCCore
22 from litex.soc.integration.soc_sdram import SoCSDRAM
23 from litex.soc.integration.builder import Builder
24 from litex.soc.integration.common import get_mem_data
25
26 from litedram import modules as litedram_modules
27 from litedram.phy.model import SDRAMPHYModel
28 from litex.tools.litex_sim import sdram_module_nphases, get_sdram_phy_settings
29
30 from litex.tools.litex_sim import Platform
31
32 from libresoc import LibreSoC
33 from microwatt import Microwatt
34
35 # HACK!
36 from litex.soc.integration.soc import SoCCSRHandler
37 SoCCSRHandler.supported_address_width.append(12)
38
39 # LibreSoCSim -----------------------------------------------------------------
40
41 class LibreSoCSim(SoCSDRAM):
42 def __init__(self, cpu="libresoc", variant="standardjtag", debug=False,
43 with_sdram=True,
44 sdram_module = "AS4C16M16",
45 #sdram_data_width = 16,
46 #sdram_module = "MT48LC16M16",
47 sdram_data_width = 16,
48 irq_reserved_irqs = {'uart': 0},
49 ):
50 assert cpu in ["libresoc", "microwatt"]
51 platform = Platform()
52 sys_clk_freq = int(100e6)
53
54 #ram_fname = "/home/lkcl/src/libresoc/microwatt/" \
55 # "hello_world/hello_world.bin"
56 #ram_fname = "/home/lkcl/src/libresoc/microwatt/" \
57 # "tests/1.bin"
58 #ram_fname = "/tmp/test.bin"
59 #ram_fname = "/home/lkcl/src/libresoc/microwatt/" \
60 # "micropython/firmware.bin"
61 #ram_fname = "/home/lkcl/src/libresoc/microwatt/" \
62 # "tests/xics/xics.bin"
63 #ram_fname = "/home/lkcl/src/libresoc/microwatt/" \
64 # "tests/decrementer/decrementer.bin"
65 #ram_fname = "/home/lkcl/src/libresoc/microwatt/" \
66 # "hello_world/hello_world.bin"
67 ram_fname = "/home/lkcl/src/libresoc/microwatt/" \
68 "tests/mmu/mmu.bin"
69 #ram_fname = None
70
71 # reserve XICS ICP and XICS memory addresses.
72 self.mem_map['xicsicp'] = 0xc0004000
73 self.mem_map['xicsics'] = 0xc0005000
74 self.mem_map['gpio'] = 0xc0007000
75 #self.csr_map["xicsicp"] = 8 # 8 x 0x800 == 0x4000
76 #self.csr_map["xicsics"] = 10 # 10 x 0x800 == 0x5000
77
78 ram_init = []
79 if ram_fname:
80 #ram_init = get_mem_data({
81 # ram_fname: "0x00000000",
82 # }, "little")
83 ram_init = get_mem_data(ram_fname, "little")
84
85 # remap the main RAM to reset-start-address
86 self.mem_map["main_ram"] = 0x00000000
87
88 # without sram nothing works, therefore move it to higher up
89 self.mem_map["sram"] = 0x90000000
90
91 # put UART at 0xc000200 (w00t! this works!)
92 self.csr_map["uart"] = 4
93
94
95 # SoCCore -------------------------------------------------------------
96 SoCSDRAM.__init__(self, platform, clk_freq=sys_clk_freq,
97 cpu_type = "microwatt",
98 cpu_cls = LibreSoC if cpu == "libresoc" \
99 else Microwatt,
100 #bus_data_width = 64,
101 csr_address_width = 12, # limit to 0x4000
102 cpu_variant = variant,
103 csr_data_width = 8,
104 l2_size = 0,
105 uart_name = "sim",
106 with_sdram = with_sdram,
107 sdram_module = sdram_module,
108 sdram_data_width = sdram_data_width,
109 integrated_rom_size = 0 if ram_fname else 0x10000,
110 integrated_sram_size = 0x40000,
111 #integrated_main_ram_init = ram_init,
112 integrated_main_ram_size = 0x00000000 if with_sdram \
113 else 0x10000000 , # 256MB
114 )
115 self.platform.name = "sim"
116
117 if cpu == "libresoc":
118 # XICS interrupt devices
119 icp_addr = self.mem_map['xicsicp']
120 icp_wb = self.cpu.xics_icp
121 icp_region = SoCRegion(origin=icp_addr, size=0x20, cached=False)
122 self.bus.add_slave(name='xicsicp', slave=icp_wb, region=icp_region)
123
124 ics_addr = self.mem_map['xicsics']
125 ics_wb = self.cpu.xics_ics
126 ics_region = SoCRegion(origin=ics_addr, size=0x1000, cached=False)
127 self.bus.add_slave(name='xicsics', slave=ics_wb, region=ics_region)
128
129 if "gpio" in variant:
130 # Simple GPIO peripheral
131 gpio_addr = self.mem_map['gpio']
132 gpio_wb = self.cpu.simple_gpio
133 gpio_region = SoCRegion(origin=gpio_addr, size=0x20, cached=False)
134 self.bus.add_slave(name='gpio', slave=gpio_wb, region=gpio_region)
135
136
137 # CRG -----------------------------------------------------------------
138 self.submodules.crg = CRG(platform.request("sys_clk"))
139
140 #ram_init = []
141
142 # SDRAM ----------------------------------------------------
143 if with_sdram:
144 sdram_clk_freq = int(100e6) # FIXME: use 100MHz timings
145 sdram_module_cls = getattr(litedram_modules, sdram_module)
146 sdram_rate = "1:{}".format(
147 sdram_module_nphases[sdram_module_cls.memtype])
148 sdram_module = sdram_module_cls(sdram_clk_freq, sdram_rate)
149 phy_settings = get_sdram_phy_settings(
150 memtype = sdram_module.memtype,
151 data_width = sdram_data_width,
152 clk_freq = sdram_clk_freq)
153 self.submodules.sdrphy = SDRAMPHYModel(sdram_module,
154 phy_settings,
155 init=ram_init
156 )
157 self.register_sdram(
158 self.sdrphy,
159 sdram_module.geom_settings,
160 sdram_module.timing_settings)
161 # FIXME: skip memtest to avoid corrupting memory
162 self.add_constant("MEMTEST_BUS_SIZE", 128//16)
163 self.add_constant("MEMTEST_DATA_SIZE", 128//16)
164 self.add_constant("MEMTEST_ADDR_SIZE", 128//16)
165 self.add_constant("MEMTEST_BUS_DEBUG", 1)
166 self.add_constant("MEMTEST_ADDR_DEBUG", 1)
167 self.add_constant("MEMTEST_DATA_DEBUG", 1)
168
169
170 if "jtag" in variant:
171 # add JTAG platform pins
172 platform.add_extension([
173 ("jtag", 0,
174 Subsignal("tck", Pins(1)),
175 Subsignal("tms", Pins(1)),
176 Subsignal("tdi", Pins(1)),
177 Subsignal("tdo", Pins(1)),
178 )
179 ])
180
181 jtagpads = platform.request("jtag")
182 self.comb += self.cpu.jtag_tck.eq(jtagpads.tck)
183 self.comb += self.cpu.jtag_tms.eq(jtagpads.tms)
184 self.comb += self.cpu.jtag_tdi.eq(jtagpads.tdi)
185 self.comb += jtagpads.tdo.eq(self.cpu.jtag_tdo)
186
187
188 # Debug ---------------------------------------------------------------
189 # (enable with ./sim.py --debug --variant=standard)
190 if not debug:
191 return
192
193 # In debug mode, the DMI interface is used to perform single-step
194 # and dump of the full register set (MSR, r0-r31, CR, XER, PC).
195 # by running the exact same program with microwatt and libre-soc
196 # a straight "diff -u" of the complete progress dumps can be done
197 # and therefore computation instruction discrepancies found immediately
198 # and easily, running at "verilator" speed.
199 #
200 # the FSM is a bit of a dog's dinner, it relies on the way that DMI
201 # works, sending requests at periodic intervals. needs work. DoesTheJob.
202
203 # setup running of DMI FSM
204 dmi_addr = Signal(4)
205 dmi_din = Signal(64)
206 dmi_dout = Signal(64)
207 dmi_wen = Signal(1)
208 dmi_req = Signal(1)
209
210 # debug log out
211 dbg_addr = Signal(4)
212 dbg_dout = Signal(64)
213 dbg_msg = Signal(1)
214
215 # capture pc from dmi
216 pc = Signal(64)
217 active_dbg = Signal()
218 active_dbg_cr = Signal()
219 active_dbg_xer = Signal()
220
221 # xer flags
222 xer_so = Signal()
223 xer_ca = Signal()
224 xer_ca32 = Signal()
225 xer_ov = Signal()
226 xer_ov32 = Signal()
227
228 # increment counter, Stop after 100000 cycles
229 uptime = Signal(64)
230 self.sync += uptime.eq(uptime + 1)
231 #self.sync += If(uptime == 1000000000000, Finish())
232
233 # DMI FSM counter and FSM itself
234 dmicount = Signal(10)
235 dmirunning = Signal(1)
236 dmi_monitor = Signal(1)
237 dmifsm = FSM()
238 self.submodules += dmifsm
239
240 # DMI FSM
241 dmifsm.act("START",
242 If(dmi_req & dmi_wen,
243 (self.cpu.dmi_addr.eq(dmi_addr), # DMI Addr
244 self.cpu.dmi_din.eq(dmi_din), # DMI in
245 self.cpu.dmi_req.eq(1), # DMI request
246 self.cpu.dmi_wr.eq(1), # DMI write
247 If(self.cpu.dmi_ack,
248 (NextState("IDLE"),
249 )
250 ),
251 ),
252 ),
253 If(dmi_req & ~dmi_wen,
254 (self.cpu.dmi_addr.eq(dmi_addr), # DMI Addr
255 self.cpu.dmi_req.eq(1), # DMI request
256 self.cpu.dmi_wr.eq(0), # DMI read
257 If(self.cpu.dmi_ack,
258 # acknowledge received: capture data.
259 (NextState("IDLE"),
260 NextValue(dbg_addr, dmi_addr),
261 NextValue(dbg_dout, self.cpu.dmi_dout),
262 NextValue(dbg_msg, 1),
263 ),
264 ),
265 ),
266 )
267 )
268
269 # DMI response received: reset the dmi request and check if
270 # in "monitor" mode
271 dmifsm.act("IDLE",
272 If(dmi_monitor,
273 NextState("FIRE_MONITOR"), # fire "monitor" on next cycle
274 ).Else(
275 NextState("START"), # back to start on next cycle
276 ),
277 NextValue(dmi_req, 0),
278 NextValue(dmi_addr, 0),
279 NextValue(dmi_din, 0),
280 NextValue(dmi_wen, 0),
281 )
282
283 # "monitor" mode fires off a STAT request
284 dmifsm.act("FIRE_MONITOR",
285 (NextValue(dmi_req, 1),
286 NextValue(dmi_addr, 1), # DMI STAT address
287 NextValue(dmi_din, 0),
288 NextValue(dmi_wen, 0), # read STAT
289 NextState("START"), # back to start on next cycle
290 )
291 )
292
293 self.comb += xer_so.eq((dbg_dout & 1) == 1)
294 self.comb += xer_ca.eq((dbg_dout & 4) == 4)
295 self.comb += xer_ca32.eq((dbg_dout & 8) == 8)
296 self.comb += xer_ov.eq((dbg_dout & 16) == 16)
297 self.comb += xer_ov32.eq((dbg_dout & 32) == 32)
298
299 # debug messages out
300 self.sync += If(dbg_msg,
301 (If(active_dbg & (dbg_addr == 0b10), # PC
302 Display("pc : %016x", dbg_dout),
303 ),
304 If(dbg_addr == 0b10, # PC
305 pc.eq(dbg_dout), # capture PC
306 ),
307 #If(dbg_addr == 0b11, # MSR
308 # Display(" msr: %016x", dbg_dout),
309 #),
310 If(dbg_addr == 0b1000, # CR
311 Display(" cr : %016x", dbg_dout),
312 ),
313 If(dbg_addr == 0b1001, # XER
314 Display(" xer: so %d ca %d 32 %d ov %d 32 %d",
315 xer_so, xer_ca, xer_ca32, xer_ov, xer_ov32),
316 ),
317 If(dbg_addr == 0b101, # GPR
318 Display(" gpr: %016x", dbg_dout),
319 ),
320 # also check if this is a "stat"
321 If(dbg_addr == 1, # requested a STAT
322 #Display(" stat: %x", dbg_dout),
323 If(dbg_dout & 2, # bit 2 of STAT is "stopped" mode
324 dmirunning.eq(1), # continue running
325 dmi_monitor.eq(0), # and stop monitor mode
326 ),
327 ),
328 dbg_msg.eq(0)
329 )
330 )
331
332 # kick off a "stop"
333 self.sync += If(uptime == 0,
334 (dmi_addr.eq(0), # CTRL
335 dmi_din.eq(1<<0), # STOP
336 dmi_req.eq(1),
337 dmi_wen.eq(1),
338 )
339 )
340
341 self.sync += If(uptime == 4,
342 dmirunning.eq(1),
343 )
344
345 self.sync += If(dmirunning,
346 dmicount.eq(dmicount + 1),
347 )
348
349 # loop every 1<<N cycles
350 cyclewid = 9
351
352 # get the PC
353 self.sync += If(dmicount == 4,
354 (dmi_addr.eq(0b10), # NIA
355 dmi_req.eq(1),
356 dmi_wen.eq(0),
357 )
358 )
359
360 # kick off a "step"
361 self.sync += If(dmicount == 8,
362 (dmi_addr.eq(0), # CTRL
363 dmi_din.eq(1<<3), # STEP
364 dmi_req.eq(1),
365 dmi_wen.eq(1),
366 dmirunning.eq(0), # stop counter, need to fire "monitor"
367 dmi_monitor.eq(1), # start "monitor" instead
368 )
369 )
370
371 # limit range of pc for debug reporting
372 #self.comb += active_dbg.eq((0x378c <= pc) & (pc <= 0x38d8))
373 #self.comb += active_dbg.eq((0x0 < pc) & (pc < 0x58))
374 self.comb += active_dbg.eq(1)
375
376
377 # get the MSR
378 self.sync += If(active_dbg & (dmicount == 12),
379 (dmi_addr.eq(0b11), # MSR
380 dmi_req.eq(1),
381 dmi_wen.eq(0),
382 )
383 )
384
385 if cpu == "libresoc": # XXX TODO: waiting on microwatt upstream patch
386 #self.comb += active_dbg_cr.eq((0x10300 <= pc) & (pc <= 0x12600))
387 self.comb += active_dbg_cr.eq(0)
388
389 # get the CR
390 self.sync += If(active_dbg_cr & (dmicount == 16),
391 (dmi_addr.eq(0b1000), # CR
392 dmi_req.eq(1),
393 dmi_wen.eq(0),
394 )
395 )
396
397 #self.comb += active_dbg_xer.eq((0x10300 <= pc) & (pc <= 0x1094c))
398 self.comb += active_dbg_xer.eq(active_dbg_cr)
399
400 # get the CR
401 self.sync += If(active_dbg_xer & (dmicount == 20),
402 (dmi_addr.eq(0b1001), # XER
403 dmi_req.eq(1),
404 dmi_wen.eq(0),
405 )
406 )
407
408 # read all 32 GPRs
409 for i in range(32):
410 self.sync += If(active_dbg & (dmicount == 24+(i*8)),
411 (dmi_addr.eq(0b100), # GSPR addr
412 dmi_din.eq(i), # r1
413 dmi_req.eq(1),
414 dmi_wen.eq(1),
415 )
416 )
417
418 self.sync += If(active_dbg & (dmicount == 28+(i*8)),
419 (dmi_addr.eq(0b101), # GSPR data
420 dmi_req.eq(1),
421 dmi_wen.eq(0),
422 )
423 )
424
425 # monitor bbus read/write
426 self.sync += If(active_dbg & self.cpu.dbus.stb & self.cpu.dbus.ack,
427 Display(" [%06x] dadr: %8x, we %d s %01x w %016x r: %016x",
428 #uptime,
429 0,
430 self.cpu.dbus.adr,
431 self.cpu.dbus.we,
432 self.cpu.dbus.sel,
433 self.cpu.dbus.dat_w,
434 self.cpu.dbus.dat_r
435 )
436 )
437
438 return
439
440 # monitor ibus write
441 self.sync += If(active_dbg & self.cpu.ibus.stb & self.cpu.ibus.ack &
442 self.cpu.ibus.we,
443 Display(" [%06x] iadr: %8x, s %01x w %016x",
444 #uptime,
445 0,
446 self.cpu.ibus.adr,
447 self.cpu.ibus.sel,
448 self.cpu.ibus.dat_w,
449 )
450 )
451 # monitor ibus read
452 self.sync += If(active_dbg & self.cpu.ibus.stb & self.cpu.ibus.ack &
453 ~self.cpu.ibus.we,
454 Display(" [%06x] iadr: %8x, s %01x r %016x",
455 #uptime,
456 0,
457 self.cpu.ibus.adr,
458 self.cpu.ibus.sel,
459 self.cpu.ibus.dat_r
460 )
461 )
462
463 # Build -----------------------------------------------------------------------
464
465 def main():
466 parser = argparse.ArgumentParser(description="LiteX LibreSoC CPU Sim")
467 parser.add_argument("--cpu", default="libresoc",
468 help="CPU to use: libresoc (default) or microwatt")
469 parser.add_argument("--variant", default="standardjtag",
470 help="Specify variant with different features")
471 parser.add_argument("--debug", action="store_true",
472 help="Enable debug traces")
473 parser.add_argument("--trace", action="store_true",
474 help="Enable tracing")
475 parser.add_argument("--trace-start", default=0,
476 help="Cycle to start FST tracing")
477 parser.add_argument("--trace-end", default=-1,
478 help="Cycle to end FST tracing")
479 args = parser.parse_args()
480
481 sim_config = SimConfig(default_clk="sys_clk")
482 sim_config.add_module("serial2console", "serial")
483 sim_config.add_module("jtagremote", "jtag", args={'port': 44853})
484
485 for i in range(2):
486 soc = LibreSoCSim(cpu=args.cpu, debug=args.debug, variant=args.variant)
487 builder = Builder(soc,compile_gateware = i!=0)
488 builder.build(sim_config=sim_config,
489 run = i!=0,
490 trace = args.trace,
491 trace_start = int(args.trace_start),
492 trace_end = int(args.trace_end),
493 trace_fst = 0)
494 os.chdir("../")
495
496 if __name__ == "__main__":
497 main()