-
Notifications
You must be signed in to change notification settings - Fork 555
Use Host Bridge to control debug a SoC
Being able to easily read/write the addressable space of the SoC from a Host PC with simple scripts can be very powerful and greatly help/speed up debugging.
The easiest way to add a UART bridge to a SoC is to use an unused UART of your FPGA board. If your Platform file already has a an unused UART you can just use it, otherwise you can simply add UART pins to your Platform file by adapting the following template:
("uart_bridge", 0,
Subsignal("tx", Pins("XY")),
Subsignal("rx", Pins("YX")),
IOStandard("LVCMOSXY")
),
and connect a USB/UART dongle between your FPGA board and your Host PC.
In your SoC, instantiate the UART bridge:
from litex.soc.cores import uart
uart_bridge = uart.UARTWishboneBridge(
pads = platform.request("uart_bridge"),
clk_freq = self.clk_freq,
baudrate = 115200)
self.submodules += uart_bridge
self.add_wb_master(uart_bridge.wishbone)
and generate the .csv file that will be used by the tools:
builder = Builder(soc, ..., csr_csv="csr.csv")
Once your bitstream is built and loaded to the board, start litex_server in UART mode:
$litex_server --uart --uart-port=/dev/ttyUSBX
Another very convenient way to debug a SoC is to add an Ethernet Bridge to it this allows debugging your SoC over your local ethernet network and provide better speeds.
If your Platform file already has a an unused Ethernet PHY you can just use it, otherwise you can simply add the Ethernet pins to your Platform file by adapting the following template (here for a RMII PHY):
("eth_clocks", 0,
Subsignal("ref_clk", Pins("X")),
IOStandard("LVCMOS33"),
),
("eth", 0,
Subsignal("rst_n", Pins("X")),
Subsignal("rx_data", Pins("X X")),
Subsignal("crs_dv", Pins("X")),
Subsignal("tx_en", Pins("X")),
Subsignal("tx_data", Pins("X X")),
Subsignal("mdc", Pins("X")),
Subsignal("mdio", Pins("X")),
Subsignal("rx_er", Pins("X")),
Subsignal("int_n", Pins("X")),
IOStandard("LVCMOS33")
),
For the other types of Ethernet PHYs, you can find some inspiration in LiteX-Boards.
In your SoC, instantiate the Etherbone bridge:
from liteeth.phy.rmii import LiteEthPHYRMII
self.submodules.ethphy = LiteEthPHYRGMII(
clock_pads = self.platform.request("eth_clocks"),
pads = self.platform.request("eth"))
self.add_csr("ethphy")
self.add_etherbone(
phy = self.ethphy,
ip_address = "192.168.1.50",
mac_address = "0x10e2d5000001"
)
and generate the .csv file that will be used by the tools:
builder = Builder(soc, ..., csr_csv="csr.csv")
Once your bitstream is built and loaded to the board, start litex_server in UDP mode:
$litex_server --udp --udp_ip=192.168.1.50
If you want scan your network for available Etherbone/UDP devices, you can just do:
$litex_server --udp --udp-scan --udp-ip=192.168.1.x --udp-port=1234
Etherbone scan on 192.168.1.x network:
- 192.168.1.20
- 192.168.1.50
On PCIe SoCs, the PCIe bridge is already present in the SoC since used to access the memory map of the SoC from the Host through the BAR0. The bridge is added by the following lines in the SoC definition:
self.submodules.pcie_bridge = LitePCIeWishboneBridge(self.pcie_endpoint,
base_address = self.mem_map["csr"])
self.add_wb_master(self.pcie_bridge.wishbone)
To use this bridge from the Host, make sure the SoC will generate the .csv file that will be used by the tools:
builder = Builder(soc, ..., csr_csv="csr.csv")
Then with lspci, find the location of the PCIe board on the bus, here 02:00.0
:
00:00.0 Host bridge: Intel Corporation Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers (rev 05)
[...]
00:1f.6 Ethernet controller: Intel Corporation Ethernet Connection (2) I219-V
02:00.0 Memory controller: Xilinx Corporation Device 7024
And use this to start litex_server in PCIe mode:
$litex_server --pcie --pcie-bar=02:00.0
A simple litex_cli comes with LiteX and can be used for simple and common operations: verify the bridge operation, read the identifier, read all or some registers of the SoC:
$litex_cli --help
usage: litex_cli [-h] [--port PORT] [--ident] [--regs]
LiteX Client utility
optional arguments:
-h, --help show this help message and exit
--port PORT Host bind port
--ident Dump FPGA identifier
--regs Dump FPGA registers
litex_cli --ident
Will read the SoC identifier.
litex_cli --regs
Will dump all registers of the SoC identifier.
Note: The current directory you invoke litex_cli
in must be the project directory, otherwise it will not function properly.
litex_cli
will very simple for now (but already very useful :)) and will be extended in the future to support more common operations.
You can now easily create scripts to read/write from/to the main bus of your SoC:
#!/usr/bin/env python3
from litex import RemoteClient
wb = RemoteClient()
wb.open()
# # #
# Access SDRAM
wb.write(0x40000000, 0x12345678)
value = wb.read(0x40000000)
# Access SDRAM (with wb.mems and base address)
wb.write(wb.mems.main_ram.base, 0x12345678)
value = wb.read(wb.mems.main_ram.base)
# Trigger a reset of the SoC
wb.regs.ctrl_reset.write(1)
# Dump all CSR registers of the SoC
for name, reg in wb.regs.__dict__.items():
print("0x{:08x} : 0x{:08x} {}".format(reg.addr, reg.read(), name))
[...]
# # #
wb.close()
Have a question or want to get in touch? Our IRC channel is #litex at irc.libera.chat.
- Welcome to LiteX
- LiteX's internals
- How to
- Create a minimal SoC-TODO
- Add a new Board-TODO
- Add a new Core-WIP
- Add a new CPU-WIP
- Reuse-a-(System)Verilog,-VHDL,-Amaranth,-Spinal-HDL,-Chisel-core
- Use LiteX on the Acorn CLE 215+
- Load application code the CPU(s)
- Use Host Bridges to control/debug a SoC
- Use LiteScope to debug a SoC
- JTAG/GDB Debugging with VexRiscv CPU
- JTAG/GDB Debugging with VexRiscv-SMP, NaxRiscv and VexiiRiscv CPUs
- Document a SoC
- How to (Advanced)