Sorry, you need to enable JavaScript to visit this website.

Creating a custom peripheral

I know a lot of you have been waiting for this: we're going to create a custom peripheral in the Programmable Logic (PL) portion of the Zynq-7000 device, and talk to it via one of the ARM cores! woohoo!


The github project can be found here.  Enjoy!

Again, as previous posts, I am using the Avnet Zedboard - because it's awesome (and because I don't have a ZC702 board yet).
 

Ok, first you need to create a PlanAhead project, and add a processing subsystem to it.  Follow this blog post to get to the point where we are working in EDK.  Once you have your system loaded, and configured with the ZedBoard xml definition file, come back here.
 
 
Here we see XPS all loaded with our Zynq configuration for the Zedboard.
 
In this example I have used the name zed_first_custom_hw for my project name, and proc_module for the name of my embedded system.

Now that we are all set with our Processor sub-system, we can move onto creating our custom peripheral.

To start with we are simply just going to create a default peripheral that allows for writing and reading a value to a register.  That register will hold it's value, however, so we will be able to see if it is working.

First, make sure your configuration is correct by going to Project -> Design Rule Check.  If there are no errors, then we can move on.

The Meat:

Go to Hardware -> Create or Import Custom Peripheral ...

 
You will be greeted by a wizard landing page.  Click Next.
 
On the first 'real' page you are asked if you want to Create a new peripheral template set, or import an already existing peripheral.  If you already had code you had written and wanted to add it to your design, you would pick "Import existing peripheral".  But in this case we want to pick "Create templates for a new peripheral" since we do not yet have one.  Click Next.
 
The next page asks you where you would like to place your peripheral.  We will be placing it within our project, however if you were working on a larger collaborative design you may want to place it external to your project.  Keep the default and click Next.
 
Ok, now we get to name our peripheral, create a version number, and give it a fancy description.  I used the following:
 
Name: simple_register
Version: 1.00.a
Description: The greatest peripheral in the world, all riiiiiggghhhhhtttt
 
(the reference: http://youtu.be/_lK4cX5xGiQ).

Once you are happy, click Next.

Ok, on this next page there are four options to pick from.  The AXI4 interface standard is pretty lengthy, and super powerful.  For now, we are going to use it's simplest form of AXI4-Lite.  This is a very simple register base interaction between a master (ARM Core) and slave (our super awesome peripheral).  Keep the default and click Next.

Ok, now for some real configuration options.

From here on out, you are going to want to make sure you pick the right configuration options or you will be missing important files and need to recreate your peripheral.

There are two group boxes of options.  The first talks about Master support.  Since we are but a simple register we do not need this.  We will simply be connecting to a single AXI-4 Master, thus we only need our one AXI-4 slave port.  Keep this unchecked.

The second group box has three options in it.

1. Software reset - allow for the peripheral to be reset via software control (within the ARM)
2. User Logic software register - allow for register access via the software (within the ARM)
3. Include data phase timer - not a clue ... gotta look this up.

I have chosen to to select "Software reset" and "User logic software register", and not "Include data phase timer", as I do not know what it is ha.

Software reset will allow us to reset our peripheral, and is always a nice feature to have - even if you don't think you are going to use it.

And we are going to, of course, want to actually talk to our super awesome peripheral, so we need to include the ability to talk to the registers within it.

Click Next.

Here we are going to pick the number of software registers we want to have within our device.  In this case we are just going to have a single register.  But if you wanted to say, have a simple adder peripheral, you could set this to three registers, and write to A, B and read from C to get the answer.

Set the number of registers to 1, and click Next.

 
A continued theme you will start to recognize is the increadible power, and complexity of the Xilinx tools.  This is not without good reason, don't worry.  Once you become a Zynq master, you are going to be glad you have so much control over your system.
 
On this next page, just keep the defaults.  There are additional connections on the AXI-4 interface that we could take advantage of, but for our simple peripheral there is no need.  Click Next.

The next page will prompt you about simulation files.  Since our code is so simple, and we will not be directly modifying it, we do not need any of these models.  Click Next.

 
This page is another important one.  You are going to want to make sure you select the "Generate template drive files to help you implement software interface" option.  This isn't super unnecessary, but it is going to help you understand how the ARM core talks to your peripheral.  Select the last option, and click Next.
 
Boom!  That's it.  Look over your summary page and click Finish.  It will plug away for a bit.
 
On the left side of XPS in the IP Catalog tab you will see the new addition of our BAD ASS peripheral :D
 
 

You just made yourself a hardware peripheral there fella!  Ok, let's get it into our design already!

Double click on the SIMPLE_REGISTER name and a prompt will pop up asking if you want to add it to your design, click Yes.

 
As simple IP config box will show up.  Note that there is address information shown here.  Don't touch any of that - XPS will automatically generate the correct addresses for all of it's peripherals.  No need to tweak any of these settings, just leave them alone and click OK.
 
The next pop up is asking you if you your peripheral to be connected to the processing_system7_0, or if you want to make your own connections.  Select the processing_system7_0 option, and click OK.
 
 
 
Navigate to your Bus Interfaces tap on the top of your XPS window, and notice that you now have your peripheral connected to the AXI4 bus.  COOL!
 
The last thing we need to do is copy down the address of our peripheral.  Go to the Addresses tab on the top of your XPS screen, and copy down the address of your peripheral.  Mine was 0x64800000 - yours may be different.
 
Do another Design Rule Check by going to Project -> Design Rule Check.  If everything checks out, close XPS.  You should find yourself back in PlanAhead.  As with this blog post, generate the bit stream, but clicking Generate Bitstream in the Flow Navigator.
 

Ok, once the bitstream generation is done, we can go to File-> Export Hardware and launch SDK.  How to talk to the custom peripheral here.

But ZynqGeek, what if I want to modify the code generated by this custom peripheral to do actually DO something.  Well, you need to modify this file:

C:\Xilinx\Projects\zed_first_custom_hw\zed_first_custom_hw.srcs\sources_1\edk\proc_system\pcores\simple_register_v1_00_a\hdl\vhdl\user_logic.vhd

And if you are going to bring a port out to the outside world, you will need to modify this file as well:

C:\Xilinx\Projects\zed_first_custom_hw\zed_first_custom_hw.srcs\sources_1\edk\proc_system\pcores\simple_register_v1_00_a\hdl\vhdl\simple_register.vhd

Where simple_register.vhd is actually .vhd ... happy coding!

Note: I will do a separate blog on how to modify this custom peripheral tomorrow or Wednesday.

 

Comments

I would like first thank you for your very helpful posts.

My question is about the interface between PS and PL from software perspective. In Microblaze processor, we achieve communication between HW and SW via FSL or PLB channel. For FSL channel, for instance, we use put, cput etc functions in software to access this FSL channel, which is connected to HW unit. In AXI interface, which functions are responsible to send to or receive from data from PL to software. I examined your code and Xil_In32 and Xil_Out32 commands are used. I searched lots of documents to find related documentation for this issue. I know AXI has memory mapped interface and one can send or receive data using the memory address. Is there any other functions or commands in software which helps to communicate with AXI interfaced HW ? Related Xilinx documentation is also very helpful for this.

ismail.

Hi,

you can comunicate with AXI interface HW with memory mapped :

//
// Read
//
u32 Xil_In32(u32 Addr)
{
SYNCHRONIZE_IO;
return *(volatile u32 *) Addr;
}

//
// Write
//
void Xil_Out32(u32 OutAddress, u32 Value)
{
*(volatile u32 *) OutAddress = Value;
SYNCHRONIZE_IO;
}

You can found source code on :

..\bsp\ps7_cortexa9_0\libsrc\standalone_v3_06_a\src\xil_io.c

You can read/write register or fifo hardware with this routine.

If you need more throughout with less override, to communicate with AXI interfaced HW, use DMA feature.

Kappasm.

Ismail,

He didn't show it above but there is an address tab that you can use to see the exact address that was selected and adjust the addressable size, the default is 64K. The FSL analogy for Zynq is the Accelerated Coherency Port (ACP) that is used with the Snoop Control Unit (SCU) in the ARM core and enables the device in Programmable Logic to write to L2 cache and then the processor can access it, the SCU maintains cache coherency. See link below.

http://www.xilinx.com/support/documentation/data_sheets/ds190-Zynq-7000-...

Also check out this link for everything you ever wanted to know about Zynq.

http://www.xilinx.com/support/documentation/user_guides/ug585-Zynq-7000-...

Hi,

I modified the user_logic.vhd and simple_register.vhd
in the right folders as mentioned above. Then I
generated bitstream again, but it is not using these
2 vhdl files at all! So nothing really changes.
I deliberately added some errors in the files and
did "generate bitstream" again, but it does not show
any errors!
What is the trick to tell the tool to use those
modified vhdl files?

Regards,

Pramod

Hi,

Finally I realized that I need to manually synthesize
the modified VHDL modules; and then "generate
bitstream". After that, my modifications seem to work!
Thank God, this is resolved.

Regards,

Pramod

"Finally I realized that I need to manually synthesize
the modified VHDL modules;"

Hi Pramod,

I'm new with this tools. Can you tell me how you manually synthesize the file? I have creat a project with the project file included and add my own project in it and it's not working.

Thanks for your help!

Karl

To instantiate the custom peripheral in my top level design, do I have to physically expose the axi bus to PL? It seems like this should already be done for us, but I don't see how to instantiate it without providing S_AXI_CLK, S_AXI_ARESETN, S_AXI_AWADDR, et. al. to the peripheral.

Thanks,
scaperna

I added ports to the peripheral, so that it can be connected to an external FPGA pin. Documentation and tutorials seem say you have to re-import the peripheral after this. However, I've tried this with 14.2 and 14.3, both fail to re-import the peripheral. Anyone else run into this?
I tried to manually edit the peripheral files, but I can not get it to include the Xilinx IP cores when ISE does the build from within EDK.

You need to re-import the peripheral trought XPS. I found that the simple way is to not increment the version of your peripheral. AT a point the wizard will ask you for your black boxe. You pickup your .xco file.

I hope that helping you

Hello,

I used this tutorial to creating my own custom IP.
In my IP I need a slave interface (to configure the IP) connected to a GP port and the master interface to access to the memory connected to the HP port.
I used XPS to create the interface of this custom IP.
When I connect this one to the System through two AXI interconnect IP and launch the Synthesis, I have an error message saying that more than 100% of Device resources are used because the number of Bounded IOBS used is 255 instead of the 200 available on the Zynq 7020.

I don't understand how it can used 255 IOBS with only 2 32 bits ports.

If someone has an idea or had the same issue,

Thank you

Hi........
I want to execute verilog code in zynq fpga through C language,because i want to give the input values in verilog code from the C code,means reading and writing input/output pins of the fpga from c code.....Is it possible to read and write to the pins of fpga,How to do it......Help me
Thanks in advance........

From Xilinx http://www.xilinx.com/support/documentation/ip_documentation/axi_lite_ip...

"The timeout counter is added in the design if the C_DPHASE_TIMEOUT parameter is non-zero. If C_DPHASE_TIMEOUT = 0, you must make sure that the core generates the acknowledge signals for all the transactions."

This is basically to handle some illegal access to such as non-existing slave address with a time-out, else the bus will hang.

Thank you ZynqGeek, your tutorial is very good. I have a question for you. I'd like to connect a custom vhdl component (an adder for example). I tried to instatiate it into "simple_register.vhd" such that it can communicate with the registers within my custom peripheral (3 register). I modified "user_logic.vhd" in order to send data to the adder and to receive the result. I re-imported my modified peripheral and it seems all right. Unfortunately I can't work with the zed these days, so my question is: is this procedure correct?

I tried to synthesize the design but I get this error "[Edk 166] (generate_target): Failed to execute XPS script. Please check for any errors reported by the XPS application in the console: [D:/TutorialPlanAhead/second_zed_project/second_zed_project.srcs/sources_1/edk/processing_system\__xps/pa\_processing_system_synth.tcl]
"..
I removed the vhdl code I added and the error disappeared..What's wrong?

I haven't still solved this problem, can someone help me? I modified user_logic.vhd by adding a simple process which implements a simple counter. I did "Rescan User Repositories", then "Design Rule Check" and closed XPS. Back to PlanAhead I generated top HDL, then generated bitstream. So far so good.
Next I implemented the counter by using a separate VHDL module. Now I want to interconnect it to the system. What should I do? I instantiated it inside user_logic.vhd, but synthesis fails. Same thing when I instatiate the VHDL module inside <my_pheripheral_name>.vhd.

Any help would be much appreciated!

sticken

I want create two IP ( master and slave) and i want send a data from master to slave , by using axi bus.
i want programme on C. Same one have one exemple for this ? thx a lot

Hey
I create my own IP unfortunately I don't have example and documentation in the software xsdk is it normal ? I thought the xsdk genrate the system.bit and when I launch xsdk I have too examle and documentation.
I don't understand why I don't have example and documentation. I use the tool ise 14.4.
Cordially.

Did ZynqGeek post anything on how to modify the custom peripheral? the end of the blog post suggests that it should have already been posted but I cant seem to find it. Otherwise if it hasn't, is there a good source to reference how its done with the Zynq board?

Hi ZynqGeek,
I want to add my own package file in the user logic but Xilinx synthesis tool is not able to fined the package file. I have added in the file in project but not working.
Thanks in advance.
Regards
Babar

Did you finally do the tutorial to modify the custom peripheral? I would find it very useful but I cant find it anywhere and I cant find any similar tutorial.
I'm just looking for doing something like adding a counter to the register we used.

Thank you very much!

Hello u mentiones AXI lite gives us a software register using which we can reset the peripheral by writing 0x0000000A into that register how do i find the address of this register ?

Hi!

I'd like to implement the same custom periphal using Vivado 2013.2

what are the steps?

thanks!

I'd also very much like to know how to add peripherals in Vivado. At this point its seems just about the only way is with HLS, but there is no free version of HLS.

I am doing a test in Zynq702 to test a simple LED flash every few seconds like old time doing FPGA projects in ISE9.2i, the Coding part I was all done from XPS and the Clock generator all has been set up. but every time I downloading the bit file to Zynq from iMPact, FPGA did not response what I expect to see, the LED did not even lighten up , can anyone tell me what might be happen? because I've been searching many resources but mostly are not teaching simple XPS project. Thanks

BTW, the LED I set is functional, I`m very sure it is clk problem

Hello,

First let me thank all who participate into this discussion, it's amazing to found people with all this shared information.

i have a question, i want to tutorial this work, but it's possible to implement a simple communication read / write from the 2 ARM processor and the PL ?

in other way, i want to make the first ARM write something into the PL ( maybe via a register ) using the AXI bus, and the second ARM will read this message and print it on console, because i don't have yet the Board

i hope t found a answer for my question

thank you again,

Amine

Hi everybody,
Be careful, I'm new with Zynq

I have made my own IP with the AXI Bus. I check it's address. Then i generate the bitstream, everything is going well. In SDK i have the good address. To finish, i try this code

int main(void)
{
int* addrMemoryTest_reg0;
int* addrMemoryTest_reg1;
addrMemoryTest_reg0 = (int*) XPAR_TESTMEMORY_2_0_BASEADDR;
addrMemoryTest_reg1 = (int*) (XPAR_TESTMEMORY_2_0_BASEADDR + 0x00000004);

*addrMemoryTest_reg1 = (int) 0x000000FF;

*addrMemoryTest_reg0 = (int) 0x000000FF;
*addrMemoryTest_reg0 = (int) 0x000000FE;
*addrMemoryTest_reg0 = (int) 0x000000FD;
*addrMemoryTest_reg0 = (int) 0x000000FC;
*addrMemoryTest_reg0 = (int) 0x000000FB;
*addrMemoryTest_reg0 = (int) 0x000000FA;
*addrMemoryTest_reg0 = (int) 0x000000F9;
*addrMemoryTest_reg0 = (int) 0x000000F8;

*addrMemoryTest_reg1 = 0x00000000;

return 0;
}

With the debug mode, when i arrived to the first line "*addrMemoryTest_reg1 = (int) 0x000000FF;", the code crash. The PC go back to the initialisation and start again and do exactly the same error and crash again and again. Before i was using the Xil_out32 function and it give me exactly the same error.
But with the Xil_out32 i could see that I have a data aborted flag.

Do you know what can produce this?
By advance thanks for your help

Found my mistake. My top_model was not the one exported to the SDK (because i didn't use the top_model by default). So when i put the bistream inside the FPGA, i don't put the good one.

how can we reset the ipif from SDK? how do v write to the Bus2IP_Reset signal?

thank you