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
)
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
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
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
30 from litex
.tools
.litex_sim
import Platform
32 from libresoc
import LibreSoC
33 from microwatt
import Microwatt
36 from litex
.soc
.integration
.soc
import SoCCSRHandler
37 SoCCSRHandler
.supported_address_width
.append(12)
39 # LibreSoCSim -----------------------------------------------------------------
41 class LibreSoCSim(SoCSDRAM
):
42 def __init__(self
, cpu
="libresoc", variant
="standardjtag", debug
=False,
44 sdram_module
= "AS4C16M16",
45 #sdram_data_width = 16,
46 #sdram_module = "MT48LC16M16",
47 sdram_data_width
= 16,
48 irq_reserved_irqs
= {'uart': 0},
50 assert cpu
in ["libresoc", "microwatt"]
52 sys_clk_freq
= int(100e6
)
54 #ram_fname = "/home/lkcl/src/libresoc/microwatt/" \
55 # "hello_world/hello_world.bin"
56 #ram_fname = "/home/lkcl/src/libresoc/microwatt/" \
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"
69 # reserve XICS ICP and XICS memory addresses.
70 self
.mem_map
['xicsicp'] = 0xc0004000
71 self
.mem_map
['xicsics'] = 0xc0005000
72 self
.mem_map
['gpio'] = 0xc0007000
73 #self.csr_map["xicsicp"] = 8 # 8 x 0x800 == 0x4000
74 #self.csr_map["xicsics"] = 10 # 10 x 0x800 == 0x5000
78 #ram_init = get_mem_data({
79 # ram_fname: "0x00000000",
81 ram_init
= get_mem_data(ram_fname
, "little")
83 # remap the main RAM to reset-start-address
84 self
.mem_map
["main_ram"] = 0x00000000
86 # without sram nothing works, therefore move it to higher up
87 self
.mem_map
["sram"] = 0x90000000
89 # put UART at 0xc000200 (w00t! this works!)
90 self
.csr_map
["uart"] = 4
93 # SoCCore -------------------------------------------------------------
94 SoCSDRAM
.__init
__(self
, platform
, clk_freq
=sys_clk_freq
,
95 cpu_type
= "microwatt",
96 cpu_cls
= LibreSoC
if cpu
== "libresoc" \
99 csr_address_width
= 12, # limit to 0x4000
100 cpu_variant
= variant
,
104 with_sdram
= with_sdram
,
105 sdram_module
= sdram_module
,
106 sdram_data_width
= sdram_data_width
,
107 integrated_rom_size
= 0 if ram_fname
else 0x10000,
108 integrated_sram_size
= 0x40000,
109 #integrated_main_ram_init = ram_init,
110 integrated_main_ram_size
= 0x00000000 if with_sdram \
111 else 0x10000000 , # 256MB
113 self
.platform
.name
= "sim"
115 if cpu
== "libresoc":
116 # XICS interrupt devices
117 icp_addr
= self
.mem_map
['xicsicp']
118 icp_wb
= self
.cpu
.xics_icp
119 icp_region
= SoCRegion(origin
=icp_addr
, size
=0x20, cached
=False)
120 self
.bus
.add_slave(name
='xicsicp', slave
=icp_wb
, region
=icp_region
)
122 ics_addr
= self
.mem_map
['xicsics']
123 ics_wb
= self
.cpu
.xics_ics
124 ics_region
= SoCRegion(origin
=ics_addr
, size
=0x1000, cached
=False)
125 self
.bus
.add_slave(name
='xicsics', slave
=ics_wb
, region
=ics_region
)
127 if "gpio" in variant
:
128 # Simple GPIO peripheral
129 gpio_addr
= self
.mem_map
['gpio']
130 gpio_wb
= self
.cpu
.simple_gpio
131 gpio_region
= SoCRegion(origin
=gpio_addr
, size
=0x20, cached
=False)
132 self
.bus
.add_slave(name
='gpio', slave
=gpio_wb
, region
=gpio_region
)
135 # CRG -----------------------------------------------------------------
136 self
.submodules
.crg
= CRG(platform
.request("sys_clk"))
140 # SDRAM ----------------------------------------------------
142 sdram_clk_freq
= int(100e6
) # FIXME: use 100MHz timings
143 sdram_module_cls
= getattr(litedram_modules
, sdram_module
)
144 sdram_rate
= "1:{}".format(
145 sdram_module_nphases
[sdram_module_cls
.memtype
])
146 sdram_module
= sdram_module_cls(sdram_clk_freq
, sdram_rate
)
147 phy_settings
= get_sdram_phy_settings(
148 memtype
= sdram_module
.memtype
,
149 data_width
= sdram_data_width
,
150 clk_freq
= sdram_clk_freq
)
151 self
.submodules
.sdrphy
= SDRAMPHYModel(sdram_module
,
157 sdram_module
.geom_settings
,
158 sdram_module
.timing_settings
)
159 # FIXME: skip memtest to avoid corrupting memory
160 self
.add_constant("MEMTEST_BUS_SIZE", 128//16)
161 self
.add_constant("MEMTEST_DATA_SIZE", 128//16)
162 self
.add_constant("MEMTEST_ADDR_SIZE", 128//16)
163 self
.add_constant("MEMTEST_BUS_DEBUG", 1)
164 self
.add_constant("MEMTEST_ADDR_DEBUG", 1)
165 self
.add_constant("MEMTEST_DATA_DEBUG", 1)
168 if "jtag" in variant
:
169 # add JTAG platform pins
170 platform
.add_extension([
172 Subsignal("tck", Pins(1)),
173 Subsignal("tms", Pins(1)),
174 Subsignal("tdi", Pins(1)),
175 Subsignal("tdo", Pins(1)),
179 jtagpads
= platform
.request("jtag")
180 self
.comb
+= self
.cpu
.jtag_tck
.eq(jtagpads
.tck
)
181 self
.comb
+= self
.cpu
.jtag_tms
.eq(jtagpads
.tms
)
182 self
.comb
+= self
.cpu
.jtag_tdi
.eq(jtagpads
.tdi
)
183 self
.comb
+= jtagpads
.tdo
.eq(self
.cpu
.jtag_tdo
)
186 # Debug ---------------------------------------------------------------
187 # (enable with ./sim.py --debug --variant=standard)
191 # In debug mode, the DMI interface is used to perform single-step
192 # and dump of the full register set (MSR, r0-r31, CR, XER, PC).
193 # by running the exact same program with microwatt and libre-soc
194 # a straight "diff -u" of the complete progress dumps can be done
195 # and therefore computation instruction discrepancies found immediately
196 # and easily, running at "verilator" speed.
198 # the FSM is a bit of a dog's dinner, it relies on the way that DMI
199 # works, sending requests at periodic intervals. needs work. DoesTheJob.
201 # setup running of DMI FSM
204 dmi_dout
= Signal(64)
210 dbg_dout
= Signal(64)
213 # capture pc from dmi
215 active_dbg
= Signal()
216 active_dbg_cr
= Signal()
217 active_dbg_xer
= Signal()
226 # increment counter, Stop after 100000 cycles
228 self
.sync
+= uptime
.eq(uptime
+ 1)
229 #self.sync += If(uptime == 1000000000000, Finish())
231 # DMI FSM counter and FSM itself
232 dmicount
= Signal(10)
233 dmirunning
= Signal(1)
234 dmi_monitor
= Signal(1)
236 self
.submodules
+= dmifsm
240 If(dmi_req
& dmi_wen
,
241 (self
.cpu
.dmi_addr
.eq(dmi_addr
), # DMI Addr
242 self
.cpu
.dmi_din
.eq(dmi_din
), # DMI in
243 self
.cpu
.dmi_req
.eq(1), # DMI request
244 self
.cpu
.dmi_wr
.eq(1), # DMI write
251 If(dmi_req
& ~dmi_wen
,
252 (self
.cpu
.dmi_addr
.eq(dmi_addr
), # DMI Addr
253 self
.cpu
.dmi_req
.eq(1), # DMI request
254 self
.cpu
.dmi_wr
.eq(0), # DMI read
256 # acknowledge received: capture data.
258 NextValue(dbg_addr
, dmi_addr
),
259 NextValue(dbg_dout
, self
.cpu
.dmi_dout
),
260 NextValue(dbg_msg
, 1),
267 # DMI response received: reset the dmi request and check if
271 NextState("FIRE_MONITOR"), # fire "monitor" on next cycle
273 NextState("START"), # back to start on next cycle
275 NextValue(dmi_req
, 0),
276 NextValue(dmi_addr
, 0),
277 NextValue(dmi_din
, 0),
278 NextValue(dmi_wen
, 0),
281 # "monitor" mode fires off a STAT request
282 dmifsm
.act("FIRE_MONITOR",
283 (NextValue(dmi_req
, 1),
284 NextValue(dmi_addr
, 1), # DMI STAT address
285 NextValue(dmi_din
, 0),
286 NextValue(dmi_wen
, 0), # read STAT
287 NextState("START"), # back to start on next cycle
291 self
.comb
+= xer_so
.eq((dbg_dout
& 1) == 1)
292 self
.comb
+= xer_ca
.eq((dbg_dout
& 4) == 4)
293 self
.comb
+= xer_ca32
.eq((dbg_dout
& 8) == 8)
294 self
.comb
+= xer_ov
.eq((dbg_dout
& 16) == 16)
295 self
.comb
+= xer_ov32
.eq((dbg_dout
& 32) == 32)
298 self
.sync
+= If(dbg_msg
,
299 (If(active_dbg
& (dbg_addr
== 0b10), # PC
300 Display("pc : %016x", dbg_dout
),
302 If(dbg_addr
== 0b10, # PC
303 pc
.eq(dbg_dout
), # capture PC
305 #If(dbg_addr == 0b11, # MSR
306 # Display(" msr: %016x", dbg_dout),
308 If(dbg_addr
== 0b1000, # CR
309 Display(" cr : %016x", dbg_dout
),
311 If(dbg_addr
== 0b1001, # XER
312 Display(" xer: so %d ca %d 32 %d ov %d 32 %d",
313 xer_so
, xer_ca
, xer_ca32
, xer_ov
, xer_ov32
),
315 If(dbg_addr
== 0b101, # GPR
316 Display(" gpr: %016x", dbg_dout
),
318 # also check if this is a "stat"
319 If(dbg_addr
== 1, # requested a STAT
320 #Display(" stat: %x", dbg_dout),
321 If(dbg_dout
& 2, # bit 2 of STAT is "stopped" mode
322 dmirunning
.eq(1), # continue running
323 dmi_monitor
.eq(0), # and stop monitor mode
331 self
.sync
+= If(uptime
== 0,
332 (dmi_addr
.eq(0), # CTRL
333 dmi_din
.eq(1<<0), # STOP
339 self
.sync
+= If(uptime
== 4,
343 self
.sync
+= If(dmirunning
,
344 dmicount
.eq(dmicount
+ 1),
347 # loop every 1<<N cycles
351 self
.sync
+= If(dmicount
== 4,
352 (dmi_addr
.eq(0b10), # NIA
359 self
.sync
+= If(dmicount
== 8,
360 (dmi_addr
.eq(0), # CTRL
361 dmi_din
.eq(1<<3), # STEP
364 dmirunning
.eq(0), # stop counter, need to fire "monitor"
365 dmi_monitor
.eq(1), # start "monitor" instead
369 # limit range of pc for debug reporting
370 #self.comb += active_dbg.eq((0x378c <= pc) & (pc <= 0x38d8))
371 #self.comb += active_dbg.eq((0x0 < pc) & (pc < 0x58))
372 self
.comb
+= active_dbg
.eq(1)
376 self
.sync
+= If(active_dbg
& (dmicount
== 12),
377 (dmi_addr
.eq(0b11), # MSR
383 if cpu
== "libresoc": # XXX TODO: waiting on microwatt upstream patch
384 #self.comb += active_dbg_cr.eq((0x10300 <= pc) & (pc <= 0x12600))
385 self
.comb
+= active_dbg_cr
.eq(0)
388 self
.sync
+= If(active_dbg_cr
& (dmicount
== 16),
389 (dmi_addr
.eq(0b1000), # CR
395 #self.comb += active_dbg_xer.eq((0x10300 <= pc) & (pc <= 0x1094c))
396 self
.comb
+= active_dbg_xer
.eq(active_dbg_cr
)
399 self
.sync
+= If(active_dbg_xer
& (dmicount
== 20),
400 (dmi_addr
.eq(0b1001), # XER
408 self
.sync
+= If(active_dbg
& (dmicount
== 24+(i
*8)),
409 (dmi_addr
.eq(0b100), # GSPR addr
416 self
.sync
+= If(active_dbg
& (dmicount
== 28+(i
*8)),
417 (dmi_addr
.eq(0b101), # GSPR data
423 # monitor bbus read/write
424 self
.sync
+= If(active_dbg
& self
.cpu
.dbus
.stb
& self
.cpu
.dbus
.ack
,
425 Display(" [%06x] dadr: %8x, we %d s %01x w %016x r: %016x",
439 self
.sync
+= If(active_dbg
& self
.cpu
.ibus
.stb
& self
.cpu
.ibus
.ack
&
441 Display(" [%06x] iadr: %8x, s %01x w %016x",
450 self
.sync
+= If(active_dbg
& self
.cpu
.ibus
.stb
& self
.cpu
.ibus
.ack
&
452 Display(" [%06x] iadr: %8x, s %01x r %016x",
461 # Build -----------------------------------------------------------------------
464 parser
= argparse
.ArgumentParser(description
="LiteX LibreSoC CPU Sim")
465 parser
.add_argument("--cpu", default
="libresoc",
466 help="CPU to use: libresoc (default) or microwatt")
467 parser
.add_argument("--variant", default
="standardjtag",
468 help="Specify variant with different features")
469 parser
.add_argument("--debug", action
="store_true",
470 help="Enable debug traces")
471 parser
.add_argument("--trace", action
="store_true",
472 help="Enable tracing")
473 parser
.add_argument("--trace-start", default
=0,
474 help="Cycle to start FST tracing")
475 parser
.add_argument("--trace-end", default
=-1,
476 help="Cycle to end FST tracing")
477 args
= parser
.parse_args()
479 sim_config
= SimConfig(default_clk
="sys_clk")
480 sim_config
.add_module("serial2console", "serial")
481 sim_config
.add_module("jtagremote", "jtag", args
={'port': 44853})
484 soc
= LibreSoCSim(cpu
=args
.cpu
, debug
=args
.debug
, variant
=args
.variant
)
485 builder
= Builder(soc
,compile_gateware
= i
!=0)
486 builder
.build(sim_config
=sim_config
,
489 trace_start
= int(args
.trace_start
),
490 trace_end
= int(args
.trace_end
),
494 if __name__
== "__main__":