Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix AXI version of the Zynq7000 busses and add mapped connect fuction #1989

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

JoyBed
Copy link
Contributor

@JoyBed JoyBed commented Jun 16, 2024

This PR fixes the bugs I was encountering while playing around with PS7 block connected to the soft SoC, it also adds a way to connect the softcore to the PS7 using the connect_mapped function. The AXI4 lacks WID which is needed in AXI3 in some rare cases. PR to litex-boards will also be created with the files for any Zynq7000 board to have a way to use the PS7 blocks DDR as MAIN_RAM for the soft SoC. I successfully use it to boot Debian on RV64GC softcores.

The absence of WID signal in AXI4 when compared to AXI3 can sometimes cause problems.
It allows connecting two different busses that has different memory mappings. Usefull when softcore in PL of Zynq7000 wants to access the DDR memory of the PS7 block.
Add connect_mapped function
@trabucayre
Copy link
Collaborator

Hi,
Your PR is partially applied with this commit.
For second part: it's a new feature, I'm interested by an use case to have a better idea. Thanks!

@Dolu1990
Copy link
Collaborator

For second part: it's a new feature, I'm interested by an use case to have a better idea. Thanks!

I think this was used to remap the memory space :

  • VexiiRiscv using 0x40000000 as start address for the main memory
  • Zynq PS block starting at address 0x10000 (something like this)
  • Need to remove (0x40000000-0x10000) to the address when things goes from vexii to the zynq PS block

@FlyGoat
Copy link
Contributor

FlyGoat commented Jun 22, 2024

I would love to see such functions so we can utilize PS memory as main_ram and debug soft-core from PS side.
Potentially I can work on litex_server on PS side.

@JoyBed
Copy link
Contributor Author

JoyBed commented Jun 24, 2024

I would love to see such functions so we can utilize PS memory as main_ram and debug soft-core from PS side. Potentially I can work on litex_server on PS side.

I use it like this, this is part of my board target file:

            cpu_cls = cpu.CPUS["zynq7000"]
            zynq = cpu_cls(self.platform, "standard") # zynq7000 has no variants
            zynq.set_ps7(name="ps", config = platform.ps7_config)
            #axi_M_GP0 = zynq.add_axi_gp_master()   # Uncomment only if you want the ARM cores to have access to the IO bus
            #self.bus.add_master(master=axi_M_GP0) # of the softcore, otherwise having it commented gains better timings
            axi_S_HP0     = zynq.add_axi_hp_slave(clock_domain = self.crg.cd_sys.name)
            axi_S_HP1     = zynq.add_axi_hp_slave(clock_domain = self.crg.cd_sys.name)
            axi_S_HP2     = zynq.add_axi_hp_slave(clock_domain = self.crg.cd_sys.name)
            axi_S_HP3     = zynq.add_axi_hp_slave(clock_domain = self.crg.cd_sys.name)
            axi_S_GP0     = zynq.add_axi_gp_slave(clock_domain = self.crg.cd_sys.name)
            hp_ports      = [axi_S_HP0, axi_S_HP1, axi_S_HP2, axi_S_HP3]

            # PS7 DDR3 Interface -----------------------------
            ddr_addr      = self.cpu.mem_map["main_ram"]
            map_fct_ddr   = lambda sig : sig - ddr_addr + 0x0010_0000
            sdram_size = 0x4000_0000
            
            if hasattr(self.cpu, "add_memory_buses"):
                self.cpu.add_memory_buses(address_width = 32, data_width = 64)
                        
            if len(self.cpu.memory_buses): # if CPU has dedicated memory bus
                print("--------Connecting DDR to direct RAM port of the softcore using HP bus.--------")
                for mem_bus in self.cpu.memory_buses:
                    i = 0
                    axi_ddr = axi.AXIInterface(hp_ports[i].data_width, hp_ports[i].address_width, "byte", hp_ports[i].id_width, "axi3")
                    self.comb += axi_ddr.connect_mapped(hp_ports[i], map_fct_ddr)
                    data_width_ratio = int(axi_ddr.data_width/mem_bus.data_width)
                    print("Connecting: ", str(mem_bus), " to ", str(axi_ddr))
                    print("CPU memory bus data width: ", mem_bus.data_width, " bits")
                    print("DDR bus data width: ", axi_ddr.data_width, " bits")
                    print("CPU memory bus address width: ", mem_bus.address_width, " bits")
                    print("DDR bus address width: ", axi_ddr.address_width, " bits")
                    print("CPU memory bus id width: ", mem_bus.id_width, " bits")
                    print("DDR bus id width: ", axi_ddr.id_width, " bits")
                    # Connect directly
                    if data_width_ratio == 1:
                        print("Direct connection")
                        self.comb += mem_bus.connect(axi_ddr)
                    # UpConvert
                    elif data_width_ratio > 1:
                        print("UpConversion")
                        axi_port = axi.AXIInterface(data_width = axi_ddr.data_width, addressing="byte", id_width = len(mem_bus.aw.id))
                        self.submodules += axi.AXIUpConverter(axi_from = mem_bus, axi_to = axi_port,)
                        self.comb += axi_port.connect(axi_ddr)
                    # DownConvert
                    else:
                        print("DownConversion")
                        axi_port = axi.AXIInterface(data_width = axi_ddr.data_width, addressing="byte", id_width = len(mem_bus.aw.id))
                        self.submodules += axi.AXIDownConverter(axi_from = mem_bus, axi_to = axi_port,)
                        self.comb += axi_port.connect(axi_ddr)
                    i = i + 1
                # Add SDRAM region
                origin = None
                main_ram_region = SoCRegion(
                    origin = self.mem_map.get("main_ram", origin),
                    size   = sdram_size,
                    mode   = "rwx")
                self.bus.add_region("main_ram", main_ram_region)
            else:
                print("--------Connecting DDR to general bus of the softcore using GP bus.--------")
                axi_ddr = axi.AXIInterface(axi_S_GP0.data_width, axi_S_GP0.address_width, "byte", axi_S_GP0.id_width)
                self.comb += axi_ddr.connect_mapped(axi_S_GP0, map_fct_ddr)
                
                self.bus.add_slave(
                   name="main_ram",slave=axi_ddr,
                    region=SoCRegion(
                        origin=ddr_addr,
                        size=sdram_size,
                        mode="rwx"
                    )
                )
            print("---------------------------- End ----------------------------------------------")
            self.submodules += zynq

Very basic approach, this functionality can be merged into litex itself tho, so the argument "--with-sdram" would check if board is Zynq based or not. If no then it will create LiteDRAM, if yes than it will use that function and this code to connect the memory bus/general bus of the softCPU to the PS7 block. Sorry for the late answer, I wasnt at home because of work. If you have any questions dont hesitate to ask. And yes, the ARM cores in the PS7 and the softCPU can share the RAM with no speed bottleneck on either side and like that create a heterogenous computing system. Or having a litex_server running on the cores is also awesome idea. Please let me know.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants