Skip to content

yjlin87/pyvivado

 
 

Repository files navigation

pyvivado

A python toolbox for generation, testing and deployment of Xilinx Vivado projects.

  • Write modules in VHDL or Verilog.
  • Define module dependencies and interfaces using python.
  • Define tests in python.
  • Automate generation of Vivado projects.
  • Communicate easily with FPGA-deployed modules from python.

Warning

I am actively using this myself for projects (as of Aug 2015), but I'm not doing a very good job of making sure all the tests and documentation are up to date, or keeping the API constant. If you're thinking of using this for a real project let me know so I know somebody else is using it and can start being more careful.

Quick Start

See files simple_module.vhd, simple_module.py and qa_simple_module.py for examples of what using pyvivado looks like.

See files axi_adder.vhd, axi_adder.py and qa_axi_adder.py for a more complex example.

See github.com/benreynwar/rfgnocchi for even more examples.

Edit the file config.py. Check that the vivado variable is pointing at your vivado executable. Modify hwcodes so that it contains the hardware codes of the Xilinx devices you have connected.

Creating a New Module

Define the module using VHDL or Verilog just like normal. In this example we have two wires passing straight through the module.

library ieee;
use ieee.std_logic_1164.all;

entity SimpleModule is
  generic (
    DATA_WIDTH: positive
    );
  port (
    i_valid: in std_logic;
    i_data: in std_logic_vector(DATA_WIDTH-1 downto 0);
    o_valid: out std_logic;
    o_data: out std_logic_vector(DATA_WIDTH-1 downto 0)
    );
end SimpleModule;

architecture arch of SimpleModule is
begin
  o_valid <= i_valid;
  o_data <= i_data;
end arch;

Then create a python object that can generate or specify the required files and IP (in this case it's pretty simple).

class SimpleModuleBuilder(builder.Builder):
    
    def __init__(self, params):
        super().__init__(params)
        self.simple_filenames = [
            os.path.join(config.hdldir, 'test', 'simple_module.vhd'),
        ]

Then create a python function that generates an interface. This is the information necessary for pyvivado to create wrappers for the DUT.

def get_simple_module_interface(params):
    wires_in = (
        ('i_valid', signal.std_logic_type),
        ('i_data', signal.StdLogicVector(width=data_width)),
    )
    wires_out = (
        ('o_valid', signal.std_logic_type),
        ('o_data', signal.StdLogicVector(width=data_width)),
    )
    iface = interface.Interface(
        wires_in, wires_out, module_name='SimpleModule',
        parameters=params, builder=SimpleModuleBuilder({}),
        module_parameters={'DATA_WIDTH': params['data_width']},
	)
    return iface

Write tests in Python

Create a new testbench project that reads and writes inputs and outputs from files. The DUT is defined by the interface that is passed in.

p = project.FileTestBenchProject.create_or_update(
    interface=get_simple_module_interface({data_width: 4})
    directory=os.path.abspath('new_project'),
)

Define some input data for the DUT.

input_data = []
for i in range(100):
    input_data.append({
        'i_valid': random.randint(0, 1),
        'i_data': random.randint(0, max_data),
    })

Run a HDL simulation of the DUT with the specified input data. pyvivado takes care of generating the input file and parsing the output file.

errors, output_data = p.run_simulation(input_data)

We can now confirm that we did not get any errors and that the output data matched our expectations. If we find a bug it's easy to open up the project in the Vivado GUI to see what went wrong.

Deploy using Python

Create a project based upon a DUT with an Axi4Lite interface. The module is wrapped in the Xilinx JTAG-to-AXI block which handles communication with the host computer (this works fine for low data rates).

p = project.FPGAProject.create_or_update(
    the_builder=axi_adder.AxiAdderBuilder({}),
    parameters={'top_name': 'axi_adder', 'frequency': 100},
    directory=os.path.abspath('proj_testaxiadderfpga'),
)

Implement the project, deploy it to the FPGA, and spawn a Vivado process to communicate with it.

t_impl = p.implement()
t_impl.wait()
t_monitor, conn = p.send_to_fpga_and_monitor()

Send AXI commands to the FPGA via the monitoring Vivado process.

conn.write(address=0, data=[1,2])
conn.read(address=0, length=2)

Disclaimer

This is basically a collection of utilities that I've written to speed up my HDL development (which I'm pretty new to). There are almost certainly still hardcoded tweaks in here that are specific to my use cases and the boards I'm using. I would like to make it more generally useful, but for now, there will almost certainly be some pain to start off with. Please let me know of problems you run into!

About

Python tools for Vivado Projects

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Python 68.4%
  • VHDL 21.8%
  • Tcl 8.3%
  • Other 1.5%