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
13 from migen
import (Signal
, FSM
, If
, Display
, Finish
, NextValue
, NextState
)
14 from migen
import Display
as D
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
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
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
31 from litex
.tools
.litex_sim
import Platform
33 from libresoc
import LibreSoC
34 from microwatt
import Microwatt
37 from litex
.soc
.integration
.soc
import SoCCSRHandler
38 SoCCSRHandler
.supported_address_width
.append(12)
40 # LibreSoCSim -----------------------------------------------------------------
42 class LibreSoCSim(SoCSDRAM
):
43 def __init__(self
, cpu
="libresoc", variant
="standardjtag", debug
=False,
45 sdram_module
= "AS4C16M16",
46 #sdram_data_width = 16,
47 #sdram_module = "MT48LC16M16",
48 sdram_data_width
= 16,
49 irq_reserved_irqs
= {'uart': 0},
51 assert cpu
in ["libresoc", "microwatt"]
53 sys_clk_freq
= int(100e6
)
55 #ram_fname = "/home/lkcl/src/libresoc/microwatt/" \
56 # "hello_world/hello_world.bin"
57 #ram_fname = "/home/lkcl/src/libresoc/microwatt/" \
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/" \
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/" \
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
81 #ram_init = get_mem_data({
82 # ram_fname: "0x00000000",
84 ram_init
= get_mem_data(ram_fname
, "little")
86 # remap the main RAM to reset-start-address
87 self
.mem_map
["main_ram"] = 0x00000000
89 # without sram nothing works, therefore move it to higher up
90 self
.mem_map
["sram"] = 0x90000000
92 # put UART at 0xc000200 (w00t! this works!)
93 self
.csr_map
["uart"] = 4
96 # SoCCore -------------------------------------------------------------
97 SoCSDRAM
.__init
__(self
, platform
, clk_freq
=sys_clk_freq
,
98 cpu_type
= "microwatt",
99 cpu_cls
= LibreSoC
if cpu
== "libresoc" \
101 #bus_data_width = 64,
102 csr_address_width
= 12, # limit to 0x4000
103 cpu_variant
= variant
,
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
116 self
.platform
.name
= "sim"
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
)
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
)
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
)
138 # CRG -----------------------------------------------------------------
139 self
.submodules
.crg
= CRG(platform
.request("sys_clk"))
143 # 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
,
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)
171 if "jtag" in variant
:
172 # add JTAG platform pins
173 platform
.add_extension([
175 Subsignal("tck", Pins(1)),
176 Subsignal("tms", Pins(1)),
177 Subsignal("tdi", Pins(1)),
178 Subsignal("tdo", Pins(1)),
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
)
189 # Debug ---------------------------------------------------------------
190 # (enable with ./sim.py --debug --variant=standard)
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.
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.
204 # setup running of DMI FSM
207 dmi_dout
= Signal(64)
214 dbg_dout
= Signal(64)
217 # capture pc from dmi
219 active_dbg
= Signal()
220 active_dbg_cr
= Signal()
221 active_dbg_xer
= Signal()
230 # increment counter, Stop after 100000 cycles
232 self
.sync
+= uptime
.eq(uptime
+ 1)
233 #self.sync += If(uptime == 1000000000000, Finish())
235 # DMI FSM counter and FSM itself
236 dmicount
= Signal(10)
237 dmirunning
= Signal(1)
238 dmi_monitor
= Signal(1)
240 self
.submodules
+= dmifsm
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
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
260 # acknowledge received: capture data.
262 NextValue(dbg_addr
, dmi_addr
),
263 NextValue(dbg_dout
, self
.cpu
.dmi_dout
),
264 NextValue(dbg_msg
, 1),
271 # DMI response received: reset the dmi request and check if
275 NextState("FIRE_MONITOR"), # fire "monitor" on next cycle
277 NextState("START"), # back to start on next cycle
279 NextValue(dmi_req
, 0),
280 NextValue(dmi_addr
, 0),
281 NextValue(dmi_din
, 0),
282 NextValue(dmi_wen
, 0),
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
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)
302 self
.sync
+= If(dbg_msg
,
303 (If(active_dbg
& (dbg_addr
== 0b10), # PC
304 D("pc : %016x", dbg_dout
),
306 If(dbg_addr
== 0b10, # PC
307 pc
.eq(dbg_dout
), # capture PC
309 If(dbg_addr
== 0b11, # MSR
310 D(" msr: %016x", dbg_dout
),
312 If(dbg_addr
== 0b1000, # CR
313 D(" cr : %016x", dbg_dout
),
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
),
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
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
351 self
.sync
+= If(uptime
== 0,
352 (dmi_addr
.eq(0), # CTRL
353 dmi_din
.eq(1<<0), # STOP
359 self
.sync
+= If(uptime
== 4,
363 self
.sync
+= If(dmirunning
,
364 dmicount
.eq(dmicount
+ 1),
367 # loop every 1<<N cycles
371 self
.sync
+= If(dmicount
== 4,
372 (dmi_addr
.eq(0b10), # NIA
379 self
.sync
+= If(dmicount
== 8,
380 (dmi_addr
.eq(0), # CTRL
381 dmi_din
.eq(1<<3), # STEP
384 dmirunning
.eq(0), # stop counter, need to fire "monitor"
385 dmi_monitor
.eq(1), # start "monitor" instead
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)
395 self
.sync
+= If(active_dbg
& (dmicount
== 12),
396 (dmi_addr
.eq(0b11), # MSR
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)
407 self
.sync
+= If(active_dbg_cr
& (dmicount
== 16),
408 (dmi_addr
.eq(0b1000), # CR
414 #self.comb += active_dbg_xer.eq((0x10300 <= pc) & (pc <= 0x1094c))
415 self
.comb
+= active_dbg_xer
.eq(active_dbg_cr
)
418 self
.sync
+= If(active_dbg_xer
& (dmicount
== 20),
419 (dmi_addr
.eq(0b1001), # XER
425 # read all 32 GPRs plus the next 16 which in microwatt are
426 # the "fast" SPRs, LR, CTR, SRR0, SRR1, etc.
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)
437 self
.sync
+= If(active_dbg
& (dmicount
== 28+(i
*8)),
438 (dmi_addr
.eq(0b101), # GSPR data
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",
460 self
.sync
+= If(active_dbg
& self
.cpu
.ibus
.stb
& self
.cpu
.ibus
.ack
&
462 D(" [%06x] iadr: %8x, s %01x w %016x",
471 self
.sync
+= If(active_dbg
& self
.cpu
.ibus
.stb
& self
.cpu
.ibus
.ack
&
473 D(" [%06x] iadr: %8x, s %01x r %016x",
482 # Build -----------------------------------------------------------------------
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()
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})
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
,
510 trace_start
= int(args
.trace_start
),
511 trace_end
= int(args
.trace_end
),
515 if __name__
== "__main__":