May 20, 2022

Xilinx Vivado - blink and LED from fabric clocks, but fail trying to do it from PS

This was an Ebaz board project. It was both a success and a failure. The success was that I learned that I can blink an LED (or do anything in the FPGA) from one of the fabric clocks, and don't need a dedicated clock like the Zedboard.

The failure was that I got something screwed up (probably my business of copying files behind the scenes, although I have made that work in other situations). Whatever the case, I was unable to take the design to the SDK and work up code for the PS.

I repeated all of this more carefully immediately afterwards (the next project) and got everything to work perfectly.

My goals are:

For a clean start, I power down the ebaz and shut down Vivado. I have 2 usb cables in use. One for the pico-xvc JTAG adapter and another for the console. I also have a terminal console window running. I run xvcd-pico in yet another window (maybe someday I should automate this with a linux udev script when the pico gets plugged in.

Hack a bit on xvc-pico

I make some minor changes to xvcd-pico:

cd /u1/Projects/fpga/Ebaz/xvc-pico/daemon
vi xvcpico.c
make
make install
cd
xvcd-pico
I added the ability to parse the "-v" option (this yields vast and continual messages, so it is really of little use at present). I also make it give the following when it launches:
xvc-pico listening on port 2542

Away we go again

I launch Vivado 2019.1. I create a new project "ebaz_blink_2. It is an RTL project and I select the ebaz board. I set up a empty source file connect.v and and empty constraint file master.xdc.

Some weird buggy nonsense is going on and it is having trouble generating the empty "connect.v". I exit Vivado and start over. Now it is entirely confused about connect.v -- I go behind its back and:

cd /home/tom/ebaz_blink_2/ebaz_blink_2.srcs/sources_1/new
rm *.v
This fixes things and on we go.

Copy files from the old project

This goes sort of like this:
cd /home/tom/ebaz_blink_2/ebaz_blink_2.srcs/sources_1/new
cp /u1/home/tom/ebaz_blink_1/ebaz_blink_1.srcs/sources_1/new/*.v .
cd /home/tom/ebaz_blink_2/ebaz_blink_2.srcs/constrs_1/new
cp /u1/home/tom/ebaz_blink_1/ebaz_blink_1.srcs/constrs_1/new/*.xdc .
There ought to be a better way, but for now this will do I guess.

With both of those files in place, it is off to the IP integrator. I use "create block design" under "IP integrator". I call the design "clocks". Next I use the "+" button in the Block design diagram and add the Zynq7 processing system. Once the block appears, I click on "Run Block Automation". I accept the defaults to interface to DDR and IO and click OK. Connections to DDR and FIXED_IO appear on the diagram.

Now something new. I double click on the Zynq block, go to Clock Configuration, Advanced Clocking, and PL Fabric Clocks. I see that 4 such clocks are available. The first (FCLK_CLK0) is already enabled and set to run at 50 Hz. So I didn't really need to do any of this, but it is good to check and verify. Sure enough, the Zynq block has an output FCLK_CLK0.

Now I go to the sources tab, select "connect.v" and right click it. I click the entry "Add Module to block design" and it appears on my diagram.

Now something else new. I use the "+" button in Sources to add a design source. I call it "blinker.v" It has one input (clock) and one output (led). I add this module to the block diagram, and use the mouse to connect the Zynq FCLK to the input. I make the output external and change its name to "green_led". This will need some verilog code to divide down that 50 Mhz clock, but we will get to that a bit later.

I also make the output of the "connect" block external as "red_led".

Add an AXI gpio block

This is what will drive the connect block from a Zynq GPIO.

I use the "+" button again to add an "AXI GPIO" block to the block diagram. Now I click "run connection automation", add all, but unclick GPIO in axi_gpio_0, and click OK. This does magic, pulling in two more blocks and hooking the axi gpio block to the Zynq.

Now the axi_gpio block needs to be configured. I double click it and get a big dialog. I go to IP Configuration, select "all outputs" and make the GPIO width "1". Click OK.

Now to connect the axi gpio block to my logic. I hover next to "GPIO" on the axi_gpio block and click to make it give me a dingus to connect to, then use the mouse pencil to connect it to the input of my "connect" block.

We are basically done. I click on the arrow in a circle (regenerate layout) to make the layout look more pretty and inspect it. Then I type Ctrl-S to save it.

Now in the sources tab, under design sources, I select the design (clocks) right click to get a menu, select "create HDL wrapper", then "let vivado manage and auto update" and OK.

We need some verilog for blinker.v

I copy and modify this from another project. It looks like this:
module blinker(
    input clock,
    output led
    );

    parameter p_CNT_1HZ = 25_000_000;

    reg [99:0] r_CNT = 0;

    reg  r_TOGGLE = 1'b0;

    always @ (posedge clock)
        begin
          if (r_CNT == p_CNT_1HZ-1) // -1, since counter starts at 0
            begin
              r_TOGGLE <= !r_TOGGLE;
              r_CNT    <= 0;
            end
          else
            r_CNT <= r_CNT + 1;
        end

    assign led = r_TOGGLE;
endmodule
When I save this it warns me that "Module references are out of date" and gives me a button to "Refresh changed Modules". I click the button and everything is good.

At the very top of Vivado is an icon with some green and what looks like 4 boxes in a 2x2 arrangement. This is "generate bitstream". I click on this. This takes a long time. Note that the upper left of vivado tells you what it is doing and I see a spinning green arrow and the words "Running ...", so I know to just be patient. The long time is longer than usual with all the AXI blocks. Just keep checking what it is doing by glancing at the upper right corner.

Finally it finishes. There is a nice message "write bitstream Complete" with a green check in the upper right, along with a popup notification which I cancel to dismiss. It is important to Cancel this, or it will spend time bringing up some birds eye view of the chip that doesn't tell someone like me much (though it is very cute).

Before I forget, I use File -- Export -- Export Hardware. I take care to check "include bitstream". Exporting to local project is correct and I click OK.

What happens if I just load the FPGA?

I know the red LED won't blink without code running in the PS, but what about the PL fabric clocks that blink the green LED? Will they work without any kind of setup on the PS side? Let's find out.

I open the hardware manager and use "Open Target", then "Open new target". This gives me a "wizard" to aid and abet this process. I tell it to connect to a local target and get to a dialog that offers to Add Xilinx Virtual Cable. I click this once to get my connection added to the target list, then a second time to actually find hardware devices.

Now at the lower left (under Open Hardware Manager) I see "Program Device", which is just what I want. This loads the FPGA and immediately the green LED is blinking!

Using fabric clocks works independently of the PS! In fact the PS is still running some entirely unrelated demo code.

Off to the SDK

I use File -- Launch SDK.

This imports the hardware specification from Vivado and brings up a GUI of its own with lots of information that we ignore for now.

I use File -- New -- Application project. I give it the name "borneo" for no particular reason. I click Next to get a menu of templates and select "Hello World". This warns me that it requires a Uart IP in the hardware. I never had this problem before, why am I getting this now? I change my selection to "Empty Application" and am able to procede. Then I click Finish.

Now on the left is a file hierarchy. I expand "bornero" and then "src" and I see no C files at all. So we get wild and wooly as follows:

cd /home/tom/ebaz_blink_2/ebaz_blink_2.sdk/borneo/src
cp /u1/home/tom/ebaz_blink_1/ebaz_blink_1.sdk/ebaz_blink_1/src/*.c .
cp /u1/home/tom/ebaz_blink_1/ebaz_blink_1.sdk/ebaz_blink_1/src/*.h .
mv helloworld.c borneo.c
The problem is that the SDK seems entirely unaware of any of this.
After a lot of thrashing around, I try File -- Restart. Indeed, the SDK restarts, but nothing good.

Aha!. I select "src", then right click shows me a menu with the option "Refresh" and clicking that does the trick. Now I can double click "borneo.c", then typing Ctrl-S does a save and rebuild. All is good, maybe. Maybe not.

I try "Run as", "launch on hardware". The messages from my unrelated code in the console window stop, but no messages from the new code appear. The red LED does not blink. A popup gives me a "memory read error at 0xf8007080", which certainly is not good.

Given that this all worked fine earlier this morning, I have to call this as buggy vivado software. Why can't it do the same thing twice?

Try the SDK a second time

In order to start fresh, I do this:
cd /home/tom/ebaz_blink_2
rm -rf ebaz_blink_2.sdk

Then I go back to Vivado and redo the "export hardware" again. This creates one file:

ebaz_blink_2.sdk/clocks_wrapper.hdf
Now I launch the SDK again. And I am getting the same error when I try to select the hello world template that I need a Uart IP in the hardware. Maybe I did something inadvertant back in Vivado? Most of the Templates give the warning about needing a Uart IP, but "Peripheral Tests" does not. I select that. testperiph.c has a multitude of print() calls, so you would think it might need a uart of some kind. It builds, but fails when I try to run it.

This is all crazy nonsense that I am entirely unable to troubleshoot. (And should not have to.)


Feedback? Questions? Drop me a line!

Tom's Computer Info / tom@mmto.org