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"
67 #ram_fname = "/home/lkcl/src/libresoc/microwatt/" \
71 # reserve XICS ICP and XICS memory addresses.
72 self
.mem_map
['xicsicp'] = 0xc0004000
73 self
.mem_map
['xicsics'] = 0xc0005000
74 self
.mem_map
['gpio'] = 0xc0007000
75 #self.csr_map["xicsicp"] = 8 # 8 x 0x800 == 0x4000
76 #self.csr_map["xicsics"] = 10 # 10 x 0x800 == 0x5000
80 #ram_init = get_mem_data({
81 # ram_fname: "0x00000000",
83 ram_init
= get_mem_data(ram_fname
, "little")
85 # remap the main RAM to reset-start-address
86 self
.mem_map
["main_ram"] = 0x00000000
88 # without sram nothing works, therefore move it to higher up
89 self
.mem_map
["sram"] = 0x90000000
91 # put UART at 0xc000200 (w00t! this works!)
92 self
.csr_map
["uart"] = 4
95 # SoCCore -------------------------------------------------------------
96 SoCSDRAM
.__init
__(self
, platform
, clk_freq
=sys_clk_freq
,
97 cpu_type
= "microwatt",
98 cpu_cls
= LibreSoC
if cpu
== "libresoc" \
100 #bus_data_width = 64,
101 csr_address_width
= 12, # limit to 0x4000
102 cpu_variant
= variant
,
106 with_sdram
= with_sdram
,
107 sdram_module
= sdram_module
,
108 sdram_data_width
= sdram_data_width
,
109 integrated_rom_size
= 0 if ram_fname
else 0x10000,
110 integrated_sram_size
= 0x40000,
111 #integrated_main_ram_init = ram_init,
112 integrated_main_ram_size
= 0x00000000 if with_sdram \
113 else 0x10000000 , # 256MB
115 self
.platform
.name
= "sim"
117 if cpu
== "libresoc":
118 # XICS interrupt devices
119 icp_addr
= self
.mem_map
['xicsicp']
120 icp_wb
= self
.cpu
.xics_icp
121 icp_region
= SoCRegion(origin
=icp_addr
, size
=0x20, cached
=False)
122 self
.bus
.add_slave(name
='xicsicp', slave
=icp_wb
, region
=icp_region
)
124 ics_addr
= self
.mem_map
['xicsics']
125 ics_wb
= self
.cpu
.xics_ics
126 ics_region
= SoCRegion(origin
=ics_addr
, size
=0x1000, cached
=False)
127 self
.bus
.add_slave(name
='xicsics', slave
=ics_wb
, region
=ics_region
)
129 if "gpio" in variant
:
130 # Simple GPIO peripheral
131 gpio_addr
= self
.mem_map
['gpio']
132 gpio_wb
= self
.cpu
.simple_gpio
133 gpio_region
= SoCRegion(origin
=gpio_addr
, size
=0x20, cached
=False)
134 self
.bus
.add_slave(name
='gpio', slave
=gpio_wb
, region
=gpio_region
)
137 # CRG -----------------------------------------------------------------
138 self
.submodules
.crg
= CRG(platform
.request("sys_clk"))
142 # SDRAM ----------------------------------------------------
144 sdram_clk_freq
= int(100e6
) # FIXME: use 100MHz timings
145 sdram_module_cls
= getattr(litedram_modules
, sdram_module
)
146 sdram_rate
= "1:{}".format(
147 sdram_module_nphases
[sdram_module_cls
.memtype
])
148 sdram_module
= sdram_module_cls(sdram_clk_freq
, sdram_rate
)
149 phy_settings
= get_sdram_phy_settings(
150 memtype
= sdram_module
.memtype
,
151 data_width
= sdram_data_width
,
152 clk_freq
= sdram_clk_freq
)
153 self
.submodules
.sdrphy
= SDRAMPHYModel(sdram_module
,
159 sdram_module
.geom_settings
,
160 sdram_module
.timing_settings
)
161 # FIXME: skip memtest to avoid corrupting memory
162 self
.add_constant("MEMTEST_BUS_SIZE", 128//16)
163 self
.add_constant("MEMTEST_DATA_SIZE", 128//16)
164 self
.add_constant("MEMTEST_ADDR_SIZE", 128//16)
165 self
.add_constant("MEMTEST_BUS_DEBUG", 1)
166 self
.add_constant("MEMTEST_ADDR_DEBUG", 1)
167 self
.add_constant("MEMTEST_DATA_DEBUG", 1)
170 if "jtag" in variant
:
171 # add JTAG platform pins
172 platform
.add_extension([
174 Subsignal("tck", Pins(1)),
175 Subsignal("tms", Pins(1)),
176 Subsignal("tdi", Pins(1)),
177 Subsignal("tdo", Pins(1)),
181 jtagpads
= platform
.request("jtag")
182 self
.comb
+= self
.cpu
.jtag_tck
.eq(jtagpads
.tck
)
183 self
.comb
+= self
.cpu
.jtag_tms
.eq(jtagpads
.tms
)
184 self
.comb
+= self
.cpu
.jtag_tdi
.eq(jtagpads
.tdi
)
185 self
.comb
+= jtagpads
.tdo
.eq(self
.cpu
.jtag_tdo
)
188 # Debug ---------------------------------------------------------------
189 # (enable with ./sim.py --debug --variant=standard)
193 # In debug mode, the DMI interface is used to perform single-step
194 # and dump of the full register set (MSR, r0-r31, CR, XER, PC).
195 # by running the exact same program with microwatt and libre-soc
196 # a straight "diff -u" of the complete progress dumps can be done
197 # and therefore computation instruction discrepancies found immediately
198 # and easily, running at "verilator" speed.
200 # the FSM is a bit of a dog's dinner, it relies on the way that DMI
201 # works, sending requests at periodic intervals. needs work. DoesTheJob.
203 # setup running of DMI FSM
206 dmi_dout
= Signal(64)
213 dbg_dout
= Signal(64)
216 # capture pc from dmi
218 active_dbg
= Signal()
219 active_dbg_cr
= Signal()
220 active_dbg_xer
= Signal()
229 # increment counter, Stop after 100000 cycles
231 self
.sync
+= uptime
.eq(uptime
+ 1)
232 #self.sync += If(uptime == 1000000000000, Finish())
234 # DMI FSM counter and FSM itself
235 dmicount
= Signal(10)
236 dmirunning
= Signal(1)
237 dmi_monitor
= Signal(1)
239 self
.submodules
+= dmifsm
243 If(dmi_req
& dmi_wen
,
244 (self
.cpu
.dmi_addr
.eq(dmi_addr
), # DMI Addr
245 self
.cpu
.dmi_din
.eq(dmi_din
), # DMI in
246 self
.cpu
.dmi_req
.eq(1), # DMI request
247 self
.cpu
.dmi_wr
.eq(1), # DMI write
254 If(dmi_req
& ~dmi_wen
,
255 (self
.cpu
.dmi_addr
.eq(dmi_addr
), # DMI Addr
256 self
.cpu
.dmi_req
.eq(1), # DMI request
257 self
.cpu
.dmi_wr
.eq(0), # DMI read
259 # acknowledge received: capture data.
261 NextValue(dbg_addr
, dmi_addr
),
262 NextValue(dbg_dout
, self
.cpu
.dmi_dout
),
263 NextValue(dbg_msg
, 1),
270 # DMI response received: reset the dmi request and check if
274 NextState("FIRE_MONITOR"), # fire "monitor" on next cycle
276 NextState("START"), # back to start on next cycle
278 NextValue(dmi_req
, 0),
279 NextValue(dmi_addr
, 0),
280 NextValue(dmi_din
, 0),
281 NextValue(dmi_wen
, 0),
284 # "monitor" mode fires off a STAT request
285 dmifsm
.act("FIRE_MONITOR",
286 (NextValue(dmi_req
, 1),
287 NextValue(dmi_addr
, 1), # DMI STAT address
288 NextValue(dmi_din
, 0),
289 NextValue(dmi_wen
, 0), # read STAT
290 NextState("START"), # back to start on next cycle
294 self
.comb
+= xer_so
.eq((dbg_dout
& 1) == 1)
295 self
.comb
+= xer_ca
.eq((dbg_dout
& 4) == 4)
296 self
.comb
+= xer_ca32
.eq((dbg_dout
& 8) == 8)
297 self
.comb
+= xer_ov
.eq((dbg_dout
& 16) == 16)
298 self
.comb
+= xer_ov32
.eq((dbg_dout
& 32) == 32)
301 self
.sync
+= If(dbg_msg
,
302 (If(active_dbg
& (dbg_addr
== 0b10), # PC
303 Display("pc : %016x", dbg_dout
),
305 If(dbg_addr
== 0b10, # PC
306 pc
.eq(dbg_dout
), # capture PC
308 If(dbg_addr
== 0b11, # MSR
309 Display(" msr: %016x", dbg_dout
),
311 If(dbg_addr
== 0b1000, # CR
312 Display(" cr : %016x", dbg_dout
),
314 If(dbg_addr
== 0b1001, # XER
315 Display(" xer: so %d ca %d 32 %d ov %d 32 %d",
316 xer_so
, xer_ca
, xer_ca32
, xer_ov
, xer_ov32
),
318 If(dbg_addr
== 0b101, # GPRs (and "fast" SPRs)
319 If(regnum
<= 31, Display(" gpr%02x: %016x",
320 regnum
, dbg_dout
),), # GPRs
321 If(regnum
== 32, Display(" LR: %016x", dbg_dout
),), # LR
322 If(regnum
== 33, Display(" CTR: %016x", dbg_dout
),), # CTR
323 If(regnum
== 34, Display(" SRR0: %016x", dbg_dout
),), # SRR0
324 If(regnum
== 35, Display(" SRR1: %016x", dbg_dout
),), # SRR1
325 If(regnum
== 36, Display(" HSRR0: %016x", dbg_dout
),), # HSRR0
326 If(regnum
== 37, Display(" HSRR1: %016x", dbg_dout
),), # HSRR1
327 If(regnum
== 38, Display(" SPRG0: %016x", dbg_dout
),), # SPRG0
328 If(regnum
== 39, Display(" SPRG1: %016x", dbg_dout
),), # SPRG1
329 If(regnum
== 40, Display(" SPRG2: %016x", dbg_dout
),), # SPRG2
330 If(regnum
== 41, Display(" SPRG3: %016x", dbg_dout
),), # SPRG3
331 If(regnum
== 42, Display(" HSPRG0: %016x", dbg_dout
),), # HSPRG0
332 If(regnum
== 43, Display(" HSPRG1: %016x", dbg_dout
),), # HSPRG1
333 If(regnum
== 44, Display(" XER: %016x", dbg_dout
),), # XER
334 If(regnum
== 45, Display(" TAR: %016x", dbg_dout
),), # TAR
336 # also check if this is a "stat"
337 If(dbg_addr
== 1, # requested a STAT
338 #Display(" stat: %x", dbg_dout),
339 If(dbg_dout
& 2, # bit 2 of STAT is "stopped" mode
340 dmirunning
.eq(1), # continue running
341 dmi_monitor
.eq(0), # and stop monitor mode
349 self
.sync
+= If(uptime
== 0,
350 (dmi_addr
.eq(0), # CTRL
351 dmi_din
.eq(1<<0), # STOP
357 self
.sync
+= If(uptime
== 4,
361 self
.sync
+= If(dmirunning
,
362 dmicount
.eq(dmicount
+ 1),
365 # loop every 1<<N cycles
369 self
.sync
+= If(dmicount
== 4,
370 (dmi_addr
.eq(0b10), # NIA
377 self
.sync
+= If(dmicount
== 8,
378 (dmi_addr
.eq(0), # CTRL
379 dmi_din
.eq(1<<3), # STEP
382 dmirunning
.eq(0), # stop counter, need to fire "monitor"
383 dmi_monitor
.eq(1), # start "monitor" instead
387 # limit range of pc for debug reporting
388 #self.comb += active_dbg.eq((0x378c <= pc) & (pc <= 0x38d8))
389 #self.comb += active_dbg.eq((0x0 < pc) & (pc < 0x58))
390 self
.comb
+= active_dbg
.eq(1)
393 self
.sync
+= If(active_dbg
& (dmicount
== 12),
394 (dmi_addr
.eq(0b11), # MSR
400 if cpu
== "libresoc": # XXX TODO: waiting on microwatt upstream patch
401 #self.comb += active_dbg_cr.eq((0x10300 <= pc) & (pc <= 0x12600))
402 self
.comb
+= active_dbg_cr
.eq(0)
405 self
.sync
+= If(active_dbg_cr
& (dmicount
== 16),
406 (dmi_addr
.eq(0b1000), # CR
412 #self.comb += active_dbg_xer.eq((0x10300 <= pc) & (pc <= 0x1094c))
413 self
.comb
+= active_dbg_xer
.eq(active_dbg_cr
)
416 self
.sync
+= If(active_dbg_xer
& (dmicount
== 20),
417 (dmi_addr
.eq(0b1001), # XER
423 # read all 32 GPRs plus the next 16 which in microwatt are
424 # the "fast" SPRs, LR, CTR, SRR0, SRR1, etc.
426 self
.sync
+= If(active_dbg
& (dmicount
== 24+(i
*8)),
427 (dmi_addr
.eq(0b100), # GSPR addr
428 dmi_din
.eq(i
), # register number (0-31 GPR, 32-48 fast SPRs)
435 self
.sync
+= If(active_dbg
& (dmicount
== 28+(i
*8)),
436 (dmi_addr
.eq(0b101), # GSPR data
442 # monitor bbus read/write
443 self
.sync
+= If(active_dbg
& self
.cpu
.dbus
.stb
& self
.cpu
.dbus
.ack
,
444 Display(" [%06x] dadr: %8x, we %d s %01x w %016x r: %016x",
458 self
.sync
+= If(active_dbg
& self
.cpu
.ibus
.stb
& self
.cpu
.ibus
.ack
&
460 Display(" [%06x] iadr: %8x, s %01x w %016x",
469 self
.sync
+= If(active_dbg
& self
.cpu
.ibus
.stb
& self
.cpu
.ibus
.ack
&
471 Display(" [%06x] iadr: %8x, s %01x r %016x",
480 # Build -----------------------------------------------------------------------
483 parser
= argparse
.ArgumentParser(description
="LiteX LibreSoC CPU Sim")
484 parser
.add_argument("--cpu", default
="libresoc",
485 help="CPU to use: libresoc (default) or microwatt")
486 parser
.add_argument("--variant", default
="standardjtag",
487 help="Specify variant with different features")
488 parser
.add_argument("--debug", action
="store_true",
489 help="Enable debug traces")
490 parser
.add_argument("--trace", action
="store_true",
491 help="Enable tracing")
492 parser
.add_argument("--trace-start", default
=0,
493 help="Cycle to start FST tracing")
494 parser
.add_argument("--trace-end", default
=-1,
495 help="Cycle to end FST tracing")
496 args
= parser
.parse_args()
498 sim_config
= SimConfig(default_clk
="sys_clk")
499 sim_config
.add_module("serial2console", "serial")
500 sim_config
.add_module("jtagremote", "jtag", args
={'port': 44853})
503 soc
= LibreSoCSim(cpu
=args
.cpu
, debug
=args
.debug
, variant
=args
.variant
)
504 builder
= Builder(soc
,compile_gateware
= i
!=0)
505 builder
.build(sim_config
=sim_config
,
508 trace_start
= int(args
.trace_start
),
509 trace_end
= int(args
.trace_end
),
513 if __name__
== "__main__":