Development with Zynq – Part 5 – Programmable Logic projects with Vivado

This blog series describe the selection, set-up and usage of a development environment for Zynq.

Template project with AXI Lite support

The ARM architecture provide different processor interfaces. Zynq support AHB (Advanced High-performance Bus) and AXI (Advanced eXtensible Interface Bus).

Xilinx provide AXI Lite for setting and getting parameters of  PL components. AXI Lite a reduced AXI interface.
Xilinx XPS include a nice Create or Import Peripheral (CIP) wizard. This wizard generate VDHL code for bus connections. For AXI Lite the CIP provide a register connection to read and write PL Registers from PS.

In Vivado 2013.2 the CIP Wizard was missing. Therefore I use Xilinx XPS to design a custom IP. With the newer Vivado Version it is possible to generate custom IPs under “Create and Package IP ” => “Create new AXI4 peripherial”.

Test project with AXI Lite support

Download under axi4_lite_test.

The project include three source files

  • testbench.vhd
    Simulate a AXI Lite Master to read and write the registers
  • axi4_lite_test.vhd
    Top component with libraries to convert AXI Lite interface to a simpler bus interface
  • test_logic.vhd
    Component with registers. Custom code should be places here

In axi4_lite_test.vhd I define new port signals in the entity

  port
  (
    D_IN					:  in  std_logic_vector(7 downto 0);
    D_OUT					:  out  std_logic_vector(7 downto 0);
    InteruptOut				: out std_logic;
...

The new signals were passed to the test_logic component.

  TEST_LOGIC_I : entity work.test_logic
    generic map
    (
      C_NUM_REG                      => USER_NUM_REG,
      C_SLV_DWIDTH                   => USER_SLV_DWIDTH
    )
    port map
    (
    D_IN					=>D_IN,
    D_OUT				=>D_OUT,
    InteruptOut			=>InteruptOut,
...

In test_logic.vhd we need to add this signals to the test_logic entity, too. So we can use the signals in the architecture of test_logic. Depending on the first register slv_reg0 and the input D_IN the output D_OUT is set. The extra signal InteruptOut is for later use.

  InteruptOut<=D_IN(0);

  D_OUT_PROC : process( slv_reg0,D_IN ) is
  begin
    case slv_reg0 is
      when x"00000000" => D_OUT <= D_IN;
      when x"00000001" => D_OUT <= not (D_IN);
      when x"00000002" => D_OUT <= "10101010";
      when x"00000003" => D_OUT <= "01010101";
      when others => D_OUT <= D_IN;
    end case;
  end process D_OUT_PROC ;

Normally a read and write to registers is possible and the read deliver the value written before. I following line change this behavior for register 1. Reading register 1 deliver the value of D_IN. Because D_IN includes only 8 signals I use the rest of register 1 to reach the read size of 32 bit.

when "01000000000000000000000000000000" => slv_ip2bus_data <= slv_reg1(31 downto 8) & D_IN;

Simulationg the testbench provide the results shown in following picture

axi4_lite_test_wave

Marker:

  1. D_IN is provided as output D_OUT
  2. The value of slv_reg0 is changed to 1. D_OUT change to D_IN inverted
  3. The value of slv_reg0 is changed to 2. D_OUT change to a fix pattern
  4. The value of slv_reg0 is readed over AXI Lite
  5. The value of slv_reg1 is readed over AXI Lite. The last byte shows D_IN

Integrate your project in Zynq

Two ways are possible to bring your project in the hardware with Vivado.

  • Build a core and connect it with PS in a design block
  • Build a design block from PS, build a VHDL wrapper and connect it with your code

The second solution has the advantage that all can be integrated in one Vivado project. But much more coding is needed. For me it make sense to use a graphical tool on system level. Therefore I build a core and integrate it in Vivado.

Generating a core is very simple. Open your test project and select “Tools”=>”Package IP…”. Go further to “Choose IP Source Location”. Normally you can select “Package your project”. I have package it before, so this option is disabled. But you can insert the path to the project to open the IP package. Do not override it!

Open an new project and define your hardware. I use the zedboard. Use the tutorial creating-base-zynq-design-vivado-ipi-20132 to define a basic project.

Select “Project Settings”=>”IP” and add your your test project as repository. Your IP should be visible in the lower section.

VivadoProjectSettingsIP

Your IP is added in the IP Catalog and you can add it to your design.

VivadoAddIP_axi4_lite_test

After adding your IP an banner open. Select “Run Connection Automation” in the banner. Other IPs were added to connect your AXI interface with Zynq. Next step is to make IOs avaible from external. Select D_IN with right mouse button and select “Make External”. Do the same with D_OUT.VivadoMakeExternal

 

Last signal is InteruptOut. In any of my next post I will show how to handle a interrupt in Linux. Here we will only connect it to Zynq.  Double click on the big Zynq block  (processing_system7_1). Under “Interrupts” activate “Fabric Interrupts”and “IRQ_F2P[15:0]”.

  VivadoEnableInterrupt

On my system this feature is buggy. The activation was only visible after closing the windows. Sometimes I have to do it two or three times. After successfully activation the Zynq has a new imput “IRQ_F2P[0:0]”. Connect InteruptOut to IRQ_F2P[0:0].

VivadoResultOverview

So we get the final design block. On last information is needed before we can syntheses and implement it. We have to connect the external signals to Zynq pins. We have select the board, so the enviroment can connect  DDR and FIXED_IO automatically. Only D_IN and D_OUT are undefined.

The ucf file zedboard_master_UCF_RevC_v3.zip  from zedboard homepage can not used with Vivado. For Vivado we need a xdc file. Select “Add Sources”=>”Add or Create Constraints”=>”Create File…” to create a file named “Pins.xdc”. Copy the following information in the file and save it.

#  SW0
set_property iostandard "LVCMOS33" [get_ports "D_IN[0]"]
set_property PACKAGE_PIN "F22" [get_ports "D_IN[0]"]
set_property slew "slow" [get_ports "D_IN[0]"]
set_property drive "8" [get_ports "D_IN[0]"]
set_property PIO_DIRECTION "INPUT" [get_ports "D_IN[0]"]
#  SW1
set_property iostandard "LVCMOS33" [get_ports "D_IN[1]"]
set_property PACKAGE_PIN "G22" [get_ports "D_IN[1]"]
set_property slew "slow" [get_ports "D_IN[1]"]
set_property drive "8" [get_ports "D_IN[1]"]
set_property PIO_DIRECTION "INPUT" [get_ports "D_IN[1]"]
#  SW2
set_property iostandard "LVCMOS33" [get_ports "D_IN[2]"]
set_property PACKAGE_PIN "H22" [get_ports "D_IN[2]"]
set_property slew "slow" [get_ports "D_IN[2]"]
set_property drive "8" [get_ports "D_IN[2]"]
set_property PIO_DIRECTION "INPUT" [get_ports "D_IN[2]"]
#  SW3
set_property iostandard "LVCMOS33" [get_ports "D_IN[3]"]
set_property PACKAGE_PIN "F21" [get_ports "D_IN[3]"]
set_property slew "slow" [get_ports "D_IN[3]"]
set_property drive "8" [get_ports "D_IN[3]"]
set_property PIO_DIRECTION "INPUT" [get_ports "D_IN[3]"]
#  SW4
set_property iostandard "LVCMOS33" [get_ports "D_IN[4]"]
set_property PACKAGE_PIN "H19" [get_ports "D_IN[4]"]
set_property slew "slow" [get_ports "D_IN[4]"]
set_property drive "8" [get_ports "D_IN[4]"]
set_property PIO_DIRECTION "INPUT" [get_ports "D_IN[4]"]
#  SW5
set_property iostandard "LVCMOS33" [get_ports "D_IN[5]"]
set_property PACKAGE_PIN "H18" [get_ports "D_IN[5]"]
set_property slew "slow" [get_ports "D_IN[5]"]
set_property drive "8" [get_ports "D_IN[5]"]
set_property PIO_DIRECTION "INPUT" [get_ports "D_IN[5]"]
#  SW6
set_property iostandard "LVCMOS33" [get_ports "D_IN[6]"]
set_property PACKAGE_PIN "H17" [get_ports "D_IN[6]"]
set_property slew "slow" [get_ports "D_IN[6]"]
set_property drive "8" [get_ports "D_IN[6]"]
set_property PIO_DIRECTION "INPUT" [get_ports "D_IN[6]"]
#  SW7
set_property iostandard "LVCMOS33" [get_ports "D_IN[7]"]
set_property PACKAGE_PIN "M15" [get_ports "D_IN[7]"]
set_property slew "slow" [get_ports "D_IN[7]"]
set_property drive "8" [get_ports "D_IN[7]"]
set_property PIO_DIRECTION "INPUT" [get_ports "D_IN[7]"]

#  LED0
set_property iostandard "LVCMOS33" [get_ports "D_OUT[0]"]
set_property PACKAGE_PIN "T22" [get_ports "D_OUT[0]"]
set_property slew "slow" [get_ports "D_OUT[0]"]
set_property drive "8" [get_ports "D_OUT[0]"]
set_property PIO_DIRECTION "OUTPUT" [get_ports "D_OUT[0]"]
#  LED1
set_property iostandard "LVCMOS33" [get_ports "D_OUT[1]"]
set_property PACKAGE_PIN "T21" [get_ports "D_OUT[1]"]
set_property slew "slow" [get_ports "D_OUT[1]"]
set_property drive "8" [get_ports "D_OUT[1]"]
set_property PIO_DIRECTION "OUTPUT" [get_ports "D_OUT[1]"]
#  LED2
set_property iostandard "LVCMOS33" [get_ports "D_OUT[2]"]
set_property PACKAGE_PIN "U22" [get_ports "D_OUT[2]"]
set_property slew "slow" [get_ports "D_OUT[2]"]
set_property drive "8" [get_ports "D_OUT[2]"]
set_property PIO_DIRECTION "OUTPUT" [get_ports "D_OUT[2]"]
#  LED3
set_property iostandard "LVCMOS33" [get_ports "D_OUT[3]"]
set_property PACKAGE_PIN "U21" [get_ports "D_OUT[3]"]
set_property slew "slow" [get_ports "D_OUT[3]"]
set_property drive "8" [get_ports "D_OUT[3]"]
set_property PIO_DIRECTION "OUTPUT" [get_ports "D_OUT[3]"]
#  LED4
set_property iostandard "LVCMOS33" [get_ports "D_OUT[4]"]
set_property PACKAGE_PIN "V22" [get_ports "D_OUT[4]"]
set_property slew "slow" [get_ports "D_OUT[4]"]
set_property drive "8" [get_ports "D_OUT[4]"]
set_property PIO_DIRECTION "OUTPUT" [get_ports "D_OUT[4]"]
#  LED5
set_property iostandard "LVCMOS33" [get_ports "D_OUT[5]"]
set_property PACKAGE_PIN "W22" [get_ports "D_OUT[5]"]
set_property slew "slow" [get_ports "D_OUT[5]"]
set_property drive "8" [get_ports "D_OUT[5]"]
set_property PIO_DIRECTION "OUTPUT" [get_ports "D_OUT[5]"]
#  LED6
set_property iostandard "LVCMOS33" [get_ports "D_OUT[6]"]
set_property PACKAGE_PIN "U19" [get_ports "D_OUT[6]"]
set_property slew "slow" [get_ports "D_OUT[6]"]
set_property drive "8" [get_ports "D_OUT[6]"]
set_property PIO_DIRECTION "OUTPUT" [get_ports "D_OUT[6]"]
#  LED7
set_property iostandard "LVCMOS33" [get_ports "D_OUT[7]"]
set_property PACKAGE_PIN "U14" [get_ports "D_OUT[7]"]
set_property slew "slow" [get_ports "D_OUT[7]"]
set_property drive "8" [get_ports "D_OUT[7]"]
set_property PIO_DIRECTION "OUTPUT" [get_ports "D_OUT[7]"]

Now we are ready to build a bitfile. Use the explanation in Part 3 to build a Boot.bin file and test it.

Here are my files:

  • ZedTest
    Vivado project file
  • system.bit
    bitfile generated with ZedTest
  • BOOT.BIN
    bootfile generated from system.bit

BOOT.BIN and system.bit are included in Bootfiles.

Without software we only see that the LEDs and switches are connected (mode for slv_reg0 value 0). In the blog article Part 6 I show how to read and write registers from Linux without a device driver.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.