element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
FPGA
  • Technologies
  • More
FPGA
Blog Port a VHDL design from AMD Zynq & Pynq to Spartan-7 & MicroBlaze - part 2: software
  • Blog
  • Forum
  • Documents
  • Quiz
  • Events
  • Polls
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Join FPGA to participate - click to join for free!
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: Jan Cumps
  • Date Created: 11 May 2023 9:25 PM Date Created
  • Views 839 views
  • Likes 11 likes
  • Comments 1 comment
  • amdzynqultrasoundpulse
  • zynq
  • fpga
  • vivado
  • vhdl
  • pynq
  • amd
  • spartan
  • vitis
  • microblaze
Related
Recommended

Port a VHDL design from AMD Zynq & Pynq to Spartan-7 & MicroBlaze - part 2: software

Jan Cumps
Jan Cumps
11 May 2023

Part 2 of a little double post to document the porting of a small FPGA design from a Zynq with ARM + Linux + Pynq + Python to a smaller Spartan with MicroBlaze soft controller + bare metal.

image

In post 1, I show the Vivado design differences. Zynq uses the hard-silicon ARM microcontroller to run the software components. On Spartan-7 I use a MicroBlaze soft microcontroller core ,implemented in the FPGA fabric.

The software part of this design is very simple. The only things that the software needs to do are:

  • send configuration values to the VHDL components. These values will be used in those components to define the signals it generates. See  Learning AMD Zynq: a project to generate a set of PWM signals. 1 - problem statement and possible approach . These don't need a user interface, can be defined as constants, defaults, ...
  • send prime and fire signals the VHDL signal controller.
  • listen to some user input method to send those 2 signals. 

Comparison:

Spartan-7 Zynq with Pynq
core MicroBlaze soft microcontroller core implemented in part of the FPGA fabric real ARM Microprocessor on the same silicon as the FPGA fabric
software stack bare metal, C, and AMD HAL for FPGA IPs and MicroBlaze-FPGA interfaces Linux OS, PYNQ python wrapper around FPGA IPs and ARM-FPGA interfaces, and Jupyter web service to run user Python code
software-FPGA interface memory mapped AXI GPIO IP memory mapped AXI GPIO IP
development tool Vitis C IDE Jupyter web server that runs user Python code
Deploy Bitstream with C program loaded into FPGA fabric (or Flash) as usual (e.g. Vivado hardware manager or Vitis FPGA programmer. Linux with PYNQ run from SD card. Bitstream and user code loaded into fabric via a Jupyter web page, or PYNQ directly
Set the VHDL registers Write to a memory location (AXI GPIO) via C code Write to a memory location (AXI GPIO) via Python, in a Jupyter notebook
receive user Trigger and Fire command user buttons polled by the Microblaze firmware. The firmware writes to the VHDL (via AXI GPIO) trigger and fire flags Execute a Python command to write to the VHDL (via AXI GPIO trigger and fire flags) using a Jupyter notebook.

Spartan-7 C software in Vitis

image

The software runs on the MicroBlaze soft-microcontroller-core implemented with components of the FPGA fabric. Just like your VHDL code, it consumes gates, flip-flops, LUTs, .... of the Spartan. You write traditional microcontroller C firmware for that Microblaze, in the Vitis (Eclipse) IDE.

Drivers and configuration for the AXI GPIO registers (and other hardware designed in Vivado) are generated for you. You use Vitis HAL C APIs to interact. For the firmware developer, it feels as if you develop for a microcontroller, and the VHDL blocks are peripherals, like UART, I2C, SPI. Reading button states is reading a memory mapped register. Writing to the VHDL blocks is writing to a memory mapped location. Just like writing to (e.g. timer) registers for an ATMega controller.

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"
#include "xgpio.h"
#include "xil_io.h"

XGpio inputz, band, duty, start_delay, train_length, gate_delay, flagz;

int main()
{
	uint32_t btn;

	int Status;


	init_platform();

//	/* Initialize the GPIO driver */
//	Status = XGpio_Initialize(&inputz, XPAR_AXI_GPIO_INPUT_DEVICE_ID);
//	if (Status != XST_SUCCESS) {
//		xil_printf("Gpio Initialization Failed\r\n");
//		return XST_FAILURE;
//	}
//	XGpio_SetDataDirection(&inputz, 1, 15); // buttons are bit 0 and 1, they are inputs
//
	Status = XGpio_Initialize(&band, XPAR_AXI_GPIO_BAND_DEVICE_ID);
	if (Status != XST_SUCCESS) {
		xil_printf("Gpio Initialization Failed\r\n");
		return XST_FAILURE;
	}
	XGpio_SetDataDirection(&band, 1, 0);

	Status = XGpio_Initialize(&duty, XPAR_AXI_GPIO_DUTY_DEVICE_ID);
	if (Status != XST_SUCCESS) {
		xil_printf("Gpio Initialization Failed\r\n");
		return XST_FAILURE;
	}
	XGpio_SetDataDirection(&duty, 1, 0);

	Status = XGpio_Initialize(&start_delay, XPAR_AXI_GPIO_START_DELAY_DEVICE_ID);
	if (Status != XST_SUCCESS) {
		xil_printf("Gpio Initialization Failed\r\n");
		return XST_FAILURE;
	}
	XGpio_SetDataDirection(&start_delay, 1, 0);

	Status = XGpio_Initialize(&train_length, XPAR_AXI_GPIO_TRAIN_LENGTH_DEVICE_ID);
	if (Status != XST_SUCCESS) {
		xil_printf("Gpio Initialization Failed\r\n");
		return XST_FAILURE;
	}
	XGpio_SetDataDirection(&train_length, 1, 0);

	Status = XGpio_Initialize(&gate_delay, XPAR_AXI_GPIO_GATE_DELAY_DEVICE_ID);
	if (Status != XST_SUCCESS) {
		xil_printf("Gpio Initialization Failed\r\n");
		return XST_FAILURE;
	}
	XGpio_SetDataDirection(&gate_delay, 1, 0);

	Status = XGpio_Initialize(&flagz, XPAR_AXI_GPIO_FLAGS_DEVICE_ID);
	if (Status != XST_SUCCESS) {
		xil_printf("Gpio Initialization Failed\r\n");
		return XST_FAILURE;
	}
	XGpio_SetDataDirection(&flagz, 1, 0);

	print("Mastering MicroBlaze\n\r");

	XGpio_DiscreteWrite(&band, 1, 2);
	XGpio_DiscreteWrite(&duty, 1, 15);  // 50%
	XGpio_DiscreteWrite(&start_delay, 1, 20);
	XGpio_DiscreteWrite(&train_length, 1, 98);
	XGpio_DiscreteWrite(&gate_delay, 1, 20);


   	while(1){
   		//btn = XGpio_DiscreteRead(&inputz, 1);
    	btn = Xil_In32(XPAR_AXI_GPIO_INPUT_BASEADDR);

    	if (btn)
    	{
    		if (btn & 0b0001)
    		{
    			XGpio_DiscreteWrite(&flagz, 1, 2); // bit 1
    		}

    		if (btn & 0b0010)
			{
    			XGpio_DiscreteWrite(&flagz, 1, 1); // bit 0
			}
    	}
   	}

    cleanup_platform();
    return 0;
}

Zynq and Pynq Python software in Jupyter

image

Pynq and Linux do a whole lot of abstraction for us (and consume the best part of a real physical in-silicon ARM microcontroller - think Raspberry Pi - to do that).
Pynq Python libraries parse the Vivado hardware design and generate interfaces for the AXI GPIO registers. You can create a Jupyter notebook in your browser, and write + execute Python code that talks to those registers. That same notebook is used to load the Vivado bitsream into the FPGA.

When you load your Vivado bitstream, the hardware design gets parsed. Pynq knows at that time what registers you've created to interact with the FPGA ports. You can start writing (and reading data via Python code right away. You can interact and change at will, because Python is interpreted. A very flexible way of prototyping, testing, and learning FPGA designs.

from pynq import Overlay
ol=Overlay("pwm_ultrasound_pulser.bit")

from pynq import MMIO
RANGE = 8 # Number of bytes; 8/4 = 2x 32-bit locations which is all we need for this example

duty_address = ol.ip_dict['axi_gpio_duty']['phys_addr']
duty_register = MMIO(duty_address, RANGE) 
# Write 0x00 to the tri-state register at offset 0x4 to configure the IO as outputs.
duty_register.write(0x4, 0x0) # Write 0x0 to location 0x4; Set tri-state to output

band_address = ol.ip_dict['axi_gpio_band']['phys_addr']
band_register = MMIO(band_address, RANGE) 
# Write 0x00 to the tri-state register at offset 0x4 to configure the IO as outputs.
band_register.write(0x4, 0x0) # Write 0x0 to location 0x4; Set tri-state to output

flags_address = ol.ip_dict['axi_gpio_flags']['phys_addr']
flags_register = MMIO(flags_address, RANGE) 
# Write 0x00 to the tri-state register at offset 0x4 to configure the IO as outputs.
flags_register.write(0x4, 0x0) # Write 0x0 to location 0x4; Set tri-state to output

start_delay_address = ol.ip_dict['axi_gpio_start_delay']['phys_addr']
start_delay_register = MMIO(start_delay_address, RANGE) 
# Write 0x00 to the tri-state register at offset 0x4 to configure the IO as outputs.
start_delay_register.write(0x4, 0x0) # Write 0x0 to location 0x4; Set tri-state to output

train_length_address = ol.ip_dict['axi_gpio_train_length']['phys_addr']
train_length_register = MMIO(train_length_address, RANGE) 
# Write 0x00 to the tri-state register at offset 0x4 to configure the IO as outputs.
train_length_register.write(0x4, 0x0) # Write 0x0 to location 0x4; Set tri-state to output

gate_delay_address = ol.ip_dict['axi_gpio_gate_delay']['phys_addr']
gate_delay_register = MMIO(gate_delay_address, RANGE) 
# Write 0x00 to the tri-state register at offset 0x4 to configure the IO as outputs.
gate_delay_register.write(0x4, 0x0) # Write 0x0 to location 0x4; Set tri-state to output


def duty(duty):
    duty_register.write(0x00, duty)
    
def band(band):
    band_register.write(0x00, band)
    
def dutypct(duty):
    duty_register.write(0x00, round((0x1F*2)/(100/duty)))
    
def fire():
    flags_register.write(0x00, 1) # bit 0
    flags_register.write(0x00, 0)

def prime():
    flags_register.write(0x00, 2) # bit 1
    flags_register.write(0x00, 0)
    
def startdelay(startdelay):
    start_delay_register.write(0x0, startdelay);

def trainlength(trainlength):
    train_length_register.write(0x0, trainlength);

def gatedelay(gatedelay):
    gate_delay_register.write(0x0, gatedelay);



dutypct(50)

band(5)
startdelay(40)
trainlength(294)
gatedelay(20)
try:
    while True:
        prime()
        fire()
except KeyboardInterrupt:
    pass

note

you don't have to use Linux, PYNQ and Python on a Zynq. It happens to be the way I use my PYNQ-Z2 development board. There are many possibilities.

I could program the Zynq ARM microprocessor bare metal, with Vitis, and a program that looks very similar to the MicroBlaze firmware on the Spartan. I could run Linux, and create a Linux C program that talks to the AXI GPIO registers. In Vitis, or in a generic Linux C development environment. I could implement a MicroBlaze on the Zynq fabric, and run exactly the same hardware, etc. etc ...

  • Sign in to reply
  • Jan Cumps
    Jan Cumps over 2 years ago

    The Zynq and Spartan-7 are very different, and the price shows it. For the exercise here, they are both vastly over-specced. This design (without the controller or processor) can be built on the smallest of FPGAs.

    What you can see is that the extra oomph on both FPGAs allow for easy learning, experimenting and prototyping. The Linux + PYNQ platform on the Zynq in particular allows easy testing of designs. It's so easy to exchange signals and values with VHDL blocks. Even if you are building a small FPGA design, having this platform at hand can speed up the validation of that small design. You can create test beds to automate the checks, and to take care that all boundary cases are covered. The fast feedback I get when testing something, without the need of setting up a bench or logic analyser, has helped me to understand what the effect of subtle changes in the VHDL do.

    Simulation is important, but checking what the state is after a particular operation, is often easier if you can just poll the objects at runtime.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube