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