enjoy-digital / litex

Build your hardware, easily!

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Add example for embedding a LiteX SOC into Vivado project for Zynq

benfre opened this issue · comments

There is a few attempts to works with Xilinx SOCs with Hard ARM cores. I find these examples difficult to follow.
The example below is what i tried, and finally worked.
The principle is this: add AXI interfaces (slave or master) to LiteX soc, generate a verilog file, and put in Vivado project. That's it.

In theory you can add as many AXI interfaces as you can, remapped to different address regions, use in ARM core standalone app or as Petalinux devices.

Create SOC with AXI-lite interface

  1. Add AXI-lite pads in _io
  2. Create an AXI-lite slave interface in a LiteX soc, and connected the interface to pads,
  3. Add AXI interface as a bus master, with remapper.
  4. Use clock and reset from PS as sys clock and reset.

The AXI-lite interface only works with 32bit address width, limited in remapper to a small region.

_io = [
    ("user_led", 0, Pins(1)),
    ("user_led", 1, Pins(1)),
    ("user_led", 2, Pins(1)),
    ...
    ("axi_aclk", 0, Pins(1)),
    ("axi_arstn", 0, Pins(1)),
    ("axi",0,
        Subsignal('awvalid', Pins(1)),
        Subsignal('awready', Pins(1)),
        Subsignal('awaddr', Pins(32)),
        Subsignal('awprot', Pins(3)),
        Subsignal('wvalid', Pins(1)),
        Subsignal('wready', Pins(1)),
        Subsignal('wdata', Pins(32)),
        Subsignal('wstrb', Pins(4)),
        Subsignal('bvalid', Pins(1)),
        Subsignal('bready', Pins(1)),
        Subsignal('bresp', Pins(2)),
        Subsignal('arvalid', Pins(1)),
        Subsignal('arready', Pins(1)),
        Subsignal('araddr', Pins(32)),
        Subsignal('arprot', Pins(3)),
        Subsignal('rvalid', Pins(1)),
        Subsignal('rready', Pins(1)),
        Subsignal('rresp', Pins(2)),
        Subsignal('rdata', Pins(32)),
    ),
    ...
]

        # in CRG class, use PS clock and reset
        self.comb += ClockSignal("sys").eq(platform.request("axi_aclk"))
        self.comb += ResetSignal("sys").eq(~platform.request("axi_arstn"))

        # in SOC __init__()
        axi = axi.AXILiteInterface(data_width=32,address_width=32,clock_domain="sys",name="GP0")
        self.comb += axi.connect_to_pads(platform.request("axi"),mode="slave")
        self.bus.add_master("GP0", axi, SoCRegion(origin=0xf000_0000, size=0x0400_0000))

        # add a led_chaser to test write from external AXI 
        if with_led_chaser:
            self.leds = LedChaser(
                pads         = platform.request_all("user_led"),
                sys_clk_freq = sys_clk_freq)

Build SOC with liteX

Build soc with compiling BIOS, generating ROM files needed in SOC verilog file

python ./soc_emb.py --csr-csv csr.csv --build --no-compile-gateware

Add generated verilog file to Vivado project

Add the SOC verilog source file, drag to a block design. Example with ZYNQ7020, add as a slave under GP0, together with AXI-GPIO and AXI-IIC ip.

ZYNQ_block_desing_with_liteX

Assign embed SOC address space, (0x6000_0000in example, and size ? matched in soc)

GP0_address_assign

Make soc pads external (with same names) and copy part of xdc file generated by liteX

Generate bitstream file

Vitis app Test

An standalone app in ARM core0, call leds_out_write(7); to stop the LED Chaser.

Manually disable uart functions in csr.h, as it is conflicted with ZYNQ PS library

Manually change CSR_BASE to 0x60000000L as its assigned in Vivado

#include "generated/csr.h"

    // in main()
    leds_out_write(7);

Debug the app, LED stop chasing after led_out_write call

BIOS serial port also works

In VexRiscv, CSR is mapped in 0xF000_0000, not 0x6000_0000 :)

BIOS_read_led_chaser_CSR

If I correctly understood your attempt you wish to use the ARM processor (PS) as a master and adding some cores, connected to the AXI (cores acting as slave, PS as master). Is it true?

If so: why not using

./yourBoard.py --cpu-type zynq7000 [...]

This is working on most of the Zynq based board supported by LiteX (see digilent_zedboard.py or digilent_arty_z7.py).

If your target board hasn't been already tested/validated in this use case (as xilinx_zc706.py you have to update target file with parts specifics to zynq7000 support (again see digilent_zedboard.py as example).

I have check some zynq boards. Some zynq board use a xci file for hard zynq core (like redpitaya) , some use preset (not every board has preset) or tcl config (only see arty_z7). I am not fimilar with these methods, and not confident to generate a working PS core. I try use a self make xci, for some reason not working.

In the example I show, I can change PS ip using "Customize Block GUI" any time after genrate liteX soc.

I can then develop apps using Vitis or petalinux (in theory, haven't tried), which provide easy enviremnet for build and debugging. I understand that liteX in flavor, software build can be intergrated in Python work flow. But for beginners like me, an IDE with some template projects will get me start very quickly.

Hi @benfre,

thanks for the feedback and detailed explanations. This indeed is a nice way to integrate things and could be convenients for users familiar with Vivado Block Design. We are also using a similar approach to package/integrate different cores (ex LiteDRAM, LitePCIe, LiteEth, etc...) for users also using Vivado Block Design.

I'll have a closer look a it and if you are OK with it, this could probably be a nice tutorial to be included in the wiki!

I'm OK with this.

I'll have a closer look a it and if you are OK with it, this could probably be a nice tutorial to be included in the wiki!

Please have a closer look. I'm not familiar with migen or LiteX. This example take me couples of weeks to get working. The only AXI-lite interface and address width issue can have some fixes.