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 # add JTAG platform pins
169 platform
.add_extension([
171 Subsignal("tck", Pins(1)),
172 Subsignal("tms", Pins(1)),
173 Subsignal("tdi", Pins(1)),
174 Subsignal("tdo", Pins(1)),
178 jtagpads
= platform
.request("jtag")
179 self
.comb
+= self
.cpu
.jtag_tck
.eq(jtagpads
.tck
)
180 self
.comb
+= self
.cpu
.jtag_tms
.eq(jtagpads
.tms
)
181 self
.comb
+= self
.cpu
.jtag_tdi
.eq(jtagpads
.tdi
)
182 self
.comb
+= jtagpads
.tdo
.eq(self
.cpu
.jtag_tdo
)
185 # Debug ---------------------------------------------------------------
186 # (enable with ./sim.py --debug --variant=standard)
190 # In debug mode, the DMI interface is used to perform single-step
191 # and dump of the full register set (MSR, r0-r31, CR, XER, PC).
192 # by running the exact same program with microwatt and libre-soc
193 # a straight "diff -u" of the complete progress dumps can be done
194 # and therefore computation instruction discrepancies found immediately
195 # and easily, running at "verilator" speed.
197 # the FSM is a bit of a dog's dinner, it relies on the way that DMI
198 # works, sending requests at periodic intervals. needs work. DoesTheJob.
200 # setup running of DMI FSM
203 dmi_dout
= Signal(64)
209 dbg_dout
= Signal(64)
212 # capture pc from dmi
214 active_dbg
= Signal()
215 active_dbg_cr
= Signal()
216 active_dbg_xer
= Signal()
225 # increment counter, Stop after 100000 cycles
227 self
.sync
+= uptime
.eq(uptime
+ 1)
228 #self.sync += If(uptime == 1000000000000, Finish())
230 # DMI FSM counter and FSM itself
231 dmicount
= Signal(10)
232 dmirunning
= Signal(1)
233 dmi_monitor
= Signal(1)
235 self
.submodules
+= dmifsm
239 If(dmi_req
& dmi_wen
,
240 (self
.cpu
.dmi_addr
.eq(dmi_addr
), # DMI Addr
241 self
.cpu
.dmi_din
.eq(dmi_din
), # DMI in
242 self
.cpu
.dmi_req
.eq(1), # DMI request
243 self
.cpu
.dmi_wr
.eq(1), # DMI write
250 If(dmi_req
& ~dmi_wen
,
251 (self
.cpu
.dmi_addr
.eq(dmi_addr
), # DMI Addr
252 self
.cpu
.dmi_req
.eq(1), # DMI request
253 self
.cpu
.dmi_wr
.eq(0), # DMI read
255 # acknowledge received: capture data.
257 NextValue(dbg_addr
, dmi_addr
),
258 NextValue(dbg_dout
, self
.cpu
.dmi_dout
),
259 NextValue(dbg_msg
, 1),
266 # DMI response received: reset the dmi request and check if
270 NextState("FIRE_MONITOR"), # fire "monitor" on next cycle
272 NextState("START"), # back to start on next cycle
274 NextValue(dmi_req
, 0),
275 NextValue(dmi_addr
, 0),
276 NextValue(dmi_din
, 0),
277 NextValue(dmi_wen
, 0),
280 # "monitor" mode fires off a STAT request
281 dmifsm
.act("FIRE_MONITOR",
282 (NextValue(dmi_req
, 1),
283 NextValue(dmi_addr
, 1), # DMI STAT address
284 NextValue(dmi_din
, 0),
285 NextValue(dmi_wen
, 0), # read STAT
286 NextState("START"), # back to start on next cycle
290 self
.comb
+= xer_so
.eq((dbg_dout
& 1) == 1)
291 self
.comb
+= xer_ca
.eq((dbg_dout
& 4) == 4)
292 self
.comb
+= xer_ca32
.eq((dbg_dout
& 8) == 8)
293 self
.comb
+= xer_ov
.eq((dbg_dout
& 16) == 16)
294 self
.comb
+= xer_ov32
.eq((dbg_dout
& 32) == 32)
297 self
.sync
+= If(dbg_msg
,
298 (If(active_dbg
& (dbg_addr
== 0b10), # PC
299 Display("pc : %016x", dbg_dout
),
301 If(dbg_addr
== 0b10, # PC
302 pc
.eq(dbg_dout
), # capture PC
304 #If(dbg_addr == 0b11, # MSR
305 # Display(" msr: %016x", dbg_dout),
307 If(dbg_addr
== 0b1000, # CR
308 Display(" cr : %016x", dbg_dout
),
310 If(dbg_addr
== 0b1001, # XER
311 Display(" xer: so %d ca %d 32 %d ov %d 32 %d",
312 xer_so
, xer_ca
, xer_ca32
, xer_ov
, xer_ov32
),
314 If(dbg_addr
== 0b101, # GPR
315 Display(" gpr: %016x", dbg_dout
),
317 # also check if this is a "stat"
318 If(dbg_addr
== 1, # requested a STAT
319 #Display(" stat: %x", dbg_dout),
320 If(dbg_dout
& 2, # bit 2 of STAT is "stopped" mode
321 dmirunning
.eq(1), # continue running
322 dmi_monitor
.eq(0), # and stop monitor mode
330 self
.sync
+= If(uptime
== 0,
331 (dmi_addr
.eq(0), # CTRL
332 dmi_din
.eq(1<<0), # STOP
338 self
.sync
+= If(uptime
== 4,
342 self
.sync
+= If(dmirunning
,
343 dmicount
.eq(dmicount
+ 1),
346 # loop every 1<<N cycles
350 self
.sync
+= If(dmicount
== 4,
351 (dmi_addr
.eq(0b10), # NIA
358 self
.sync
+= If(dmicount
== 8,
359 (dmi_addr
.eq(0), # CTRL
360 dmi_din
.eq(1<<3), # STEP
363 dmirunning
.eq(0), # stop counter, need to fire "monitor"
364 dmi_monitor
.eq(1), # start "monitor" instead
368 # limit range of pc for debug reporting
369 #self.comb += active_dbg.eq((0x378c <= pc) & (pc <= 0x38d8))
370 #self.comb += active_dbg.eq((0x0 < pc) & (pc < 0x58))
371 self
.comb
+= active_dbg
.eq(1)
375 self
.sync
+= If(active_dbg
& (dmicount
== 12),
376 (dmi_addr
.eq(0b11), # MSR
382 if cpu
== "libresoc": # XXX TODO: waiting on microwatt upstream patch
383 #self.comb += active_dbg_cr.eq((0x10300 <= pc) & (pc <= 0x12600))
384 self
.comb
+= active_dbg_cr
.eq(0)
387 self
.sync
+= If(active_dbg_cr
& (dmicount
== 16),
388 (dmi_addr
.eq(0b1000), # CR
394 #self.comb += active_dbg_xer.eq((0x10300 <= pc) & (pc <= 0x1094c))
395 self
.comb
+= active_dbg_xer
.eq(active_dbg_cr
)
398 self
.sync
+= If(active_dbg_xer
& (dmicount
== 20),
399 (dmi_addr
.eq(0b1001), # XER
407 self
.sync
+= If(active_dbg
& (dmicount
== 24+(i
*8)),
408 (dmi_addr
.eq(0b100), # GSPR addr
415 self
.sync
+= If(active_dbg
& (dmicount
== 28+(i
*8)),
416 (dmi_addr
.eq(0b101), # GSPR data
422 # monitor bbus read/write
423 self
.sync
+= If(active_dbg
& self
.cpu
.dbus
.stb
& self
.cpu
.dbus
.ack
,
424 Display(" [%06x] dadr: %8x, we %d s %01x w %016x r: %016x",
438 self
.sync
+= If(active_dbg
& self
.cpu
.ibus
.stb
& self
.cpu
.ibus
.ack
&
440 Display(" [%06x] iadr: %8x, s %01x w %016x",
449 self
.sync
+= If(active_dbg
& self
.cpu
.ibus
.stb
& self
.cpu
.ibus
.ack
&
451 Display(" [%06x] iadr: %8x, s %01x r %016x",
460 # Build -----------------------------------------------------------------------
463 parser
= argparse
.ArgumentParser(description
="LiteX LibreSoC CPU Sim")
464 parser
.add_argument("--cpu", default
="libresoc",
465 help="CPU to use: libresoc (default) or microwatt")
466 parser
.add_argument("--variant", default
="standardjtag",
467 help="Specify variant with different features")
468 parser
.add_argument("--debug", action
="store_true",
469 help="Enable debug traces")
470 parser
.add_argument("--trace", action
="store_true",
471 help="Enable tracing")
472 parser
.add_argument("--trace-start", default
=0,
473 help="Cycle to start FST tracing")
474 parser
.add_argument("--trace-end", default
=-1,
475 help="Cycle to end FST tracing")
476 args
= parser
.parse_args()
478 sim_config
= SimConfig(default_clk
="sys_clk")
479 sim_config
.add_module("serial2console", "serial")
480 sim_config
.add_module("jtagremote", "jtag", args
={'port': 44853})
483 soc
= LibreSoCSim(cpu
=args
.cpu
, debug
=args
.debug
, variant
=args
.variant
)
484 builder
= Builder(soc
,compile_gateware
= i
!=0)
485 builder
.build(sim_config
=sim_config
,
488 trace_start
= int(args
.trace_start
),
489 trace_end
= int(args
.trace_end
),
493 if __name__
== "__main__":