The following example describes in detail how a Digilent ZYBO Board containing a ARM Processor and a Xilinx FPGA is configured to be used with flink. The FPGA will contain a single channel PWM module and a single channel GPIO module.
The FPGA is connected directly to the Processors AXI Interface.
Start as described in flink VHDL. Follow below directions at the specific step.
2. Under the tab Boards select Zybo Add Zybo to Boards.
8. The example shown below uses a pwmDevice, a gpioDevice, and an infoDevice.
10. Set Unique Id of the gpioDevice to 0x00000001, set Unique Id of the pwmDevices to 0x00000002 and the Base Clk to 100000000 (check how your kernel sets the PL clock).
13. Add a constraints file for the pin mapping. The ZYBO_Master.xdc file can be found here.
set_property -dict { PACKAGE_PIN B19 IOSTANDARD LVCMOS33 } [get_ports { oslv_gpios[0] }]; set_property -dict { PACKAGE_PIN B20 IOSTANDARD LVCMOS33 } [get_ports { oslv_gpios[1] }]; set_property -dict { PACKAGE_PIN A20 IOSTANDARD LVCMOS33 } [get_ports { oslv_gpios[2] }]; set_property -dict { PACKAGE_PIN E17 IOSTANDARD LVCMOS33 } [get_ports { oslv_gpios[3] }]; set_property -dict { PACKAGE_PIN T10 IOSTANDARD LVCMOS33 } [get_ports { s00_oslv_pwm[0] }]; set_property -dict { PACKAGE_PIN U12 IOSTANDARD LVCMOS33 } [get_ports { s00_oslv_pwm[1] }];
14. Dev Size for our three subdevices together is 12288.
Finally, the block diagram should look like:
(Instead of the “AXI-Interconnect” IP in the middle, it is also possible to use a “AXI-Smartconnect” IP.)
If there is no operating system used on the Zybo, flinkLite can be used to get easier access to the fpga design.
Next we will have a look at the following example code:
#include <stdio.h> #include "platform.h" #include "xil_printf.h" #include "flinklib.h" #include "xilinx/flink_axi.h" flink_dev* dev; flink_subdev* info_subdev; flink_subdev* pwm_subdev; flink_subdev* gpio_subdev; int main(){ char description[28]; int error = 0; int i = 0; init_platform(); xil_printf("fLink Example\n\r"); set_axi_base_address(XPAR_INFODEVICE_0_S00_AXI_BASEADDR); dev = flink_open(&axi_bus_ops); u32 nr_of_subdevs = flink_get_nof_subdevices(dev); xil_printf("nr_of_subdevs= %x\n\r",nr_of_subdevs); info_subdev = flink_get_subdevice_by_unique_id(dev, 0); // info device error = flink_info_get_description(info_subdev, description); xil_printf("error= %x\n\r",error); for(i = 0; i<28;i++){ xil_printf("%x,",description[i]); } xil_printf("\n\r"); pwm_subdev = flink_get_subdevice_by_unique_id(dev, 2); // pwm device uint32_t baseclk = 0; flink_pwm_get_baseclock(pwm_subdev,&baseclk); flink_pwm_set_period(pwm_subdev, 0,baseclk); flink_pwm_set_hightime(pwm_subdev, 0,baseclk/2); gpio_subdev = flink_get_subdevice_by_unique_id(dev, 1); // out gpio device flink_dio_set_direction(gpio_subdev, 0, FLINK_OUTPUT); flink_dio_set_value(gpio_subdev, 0,1); cleanup_platform(); return 0; }
The first thing we do in the code which is not given from the helloworld example is to set the base address for the bus operations. To do that we use the set_axi_base_address(base_address) method. This Address is the info devices address which we configured in the block design memory map bevore.
The next step is to open the flinkdevice with the flink_open(&bus_ops) method. The bus_ops depends on how the fpga is connected to the processor. In our example the fpga is connected to the internal axi bus of the processor. Flink allready offers the bus_ops for this bustype so we can use them.
With the flink_get_nof_subdevices(dev) method we can check how many subdevices have been found. In our case this should return 3 (info, pwm, gpio). If this is not the case, ensure that you have entered the correct value for “Dev Size” in the infoDevice. The correct value is: number of flink subdevices * 4096.
To easy access the subdevices we need a reverence to them. There are different ways to get this reference. In our example we will use the flink_get_subdevice_by_unique_id(dev, id) method. In the block design we configured the unique ids of our three blocks. So with this ids we can get easy accesses to our subdevices for example to get access to the info device we will use the method with id=0 for the pwm we use id=2.
After we have a reference to a subdevice we can use the subdevice specific methods to interact with it. In our example we us three methods to configure the pwm output. First we get the base clock frequency with wich the pwm subdevice is connected. For that we us flink_pwm_get_baseclock(pwm_subdev,&baseclk). With this frequency we can configure the pwm period and high time with the two following methods: flink_pwm_set_period(pwm_subdev, 0,baseclk), flink_pwm_set_hightime(pwm_subdev, 0,baseclk/2). In this example we set the period to baseclk which corresponds to 1sec. The hightime is set to baseclk/2 which will result in a pwm ratio of 50%
The same thing we can do with the gpio subdevice. There we only need two methods: with flink_dio_set_direction(gpio_subdev, 0, FLINK_OUTPUT) we set the direction of the gpio to output. Then we can use flink_dio_set_value(gpio_subdev, 0,1) to set it to a high state.
The Linux part works the same on all systems please checke this page.
All listed modules also include the Zynq7-Processing System, the Processor System Reset and one AXI-Smartconnect.
Module | LUT | FF |
infoDevice | 1372 | 1188 |
infoDevice, pwmDevice(1 Channel) | 4173 | 3622 |
infoDevice, watchdogDevice | 3851 | 3238 |
infoDevice, watchdogDevice, pwmDevice(1 Channel) | 4907 | 4171 |
infoDevice, watchdogDevice, pwmDevice(4 Channels) | 5180 | 4434 |
infoDevice, watchdogDevice, pwmDevice(1 Channel), ppwaDevice | 4894 | 4143 |
infoDevice, gpioDevice(4 Channels) | 3891 | 3264 |