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