May 20, 2022

Xilinx Vivado - Ebaz - blink LED in two ways.

I got into some weird buggy trouble doing this with Vivado 2019.1 and want to try it again starting from scratch. My approach now will be to do this in steps. First blink the red LED from the PS. Then modify the FPGA to add in the blinking of the green LED from the PL and fabric clocks. I want to try a couple of new things. One is to run Vivado as a full screen window to give lots of elbow room. I can use another desktop for all auxiliary windows. Actually a full screen is really too big, but setting aside an entire linux desktop for Vivado (and the SDK later) does make sense and allows them to be made nice and big.

The other thing now (not so new) is to do everything from within Vivado with little or no injection of files from behind the scenes. I may use vim to edit a file that I already have a vivado edit window looking at. This seems to work well as both vim and vivado let me know when the file has changed behind the scenes.

What we ended up with

This is one of the final results, but here it is up front as it may help a reader to see where all this is going. In the Vivado block diagram, you can right click on an empty area and the menu offers (save as PDF) at the bottom.
  • PDF of final design diagram
  • Start the project

    I create a new project "ebaz_blink_3", skip introducing any sources during project creation, and take care to select the Ebaz board.

    I use the + to add source files and add connect.v along with master.xdc as a constraint as follows:

    module connect(
        input ps_red,
        output led
        );
    
        assign led = ! ps_red;
    
    endmodule
    
    set_property -dict { PACKAGE_PIN W14 IOSTANDARD LVCMOS33 } [get_ports { red_led }];
    set_property -dict { PACKAGE_PIN W13 IOSTANDARD LVCMOS33 } [get_ports { green_led }];
    
    Now I create a block design. The default name "design_1" is as good as anything. I add the Zynq block from IP and run block automation to get DDR and IO.

    I add an AXI gpio from IP, configure it as all outputs, 1 bit wide. I run connection automation, selecting all, but omitting GPIO.

    Now I go to Sources, find "connect", right click and add it to the block diagram. I connect its input to the axi_gpio, make the output external and relabel it "red_led".

    Now the FPGA setup is basically done. In the sources tab, under design sources, I select the design (design_1) right click to get a menu, select "create HDL wrapper", then "let vivado manage and auto update" and OK.

    And I generate a bitstream. I click on the 2x2 matrix icon up top and it gets busy.

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

    On to the SDK

    I use File -- Launch SDK and there I go. I use File -- New -- Application project. I pick the name "sumatra" for no particular reason. I use Next, select the "Hello World" template, and have no weird trouble with it telling me I need an IP uart block.

    The story now is to replace the helloworld.c file. I expand sumatra and then src and double click on it to get an edit window. I right click, select rename, and call it "blinker.c".

    Now I am going behind the scenes to actually edit content.

    cd /home/tom/ebaz_blink_3/ebaz_blink_3.sdk/sumatra/src
    vi blinker.c
    
    I have to click on the "X" that you might use to close the edit window in the SDK to get the SDK to realize I have changed the content, then it looks just fine.

    Let's load things up and run them

    I pick a handy window and start xvc-pico running:
    [tom@trona ~]$ xvcd-pico
    xvc-pico listening on port 2542
    
    Then I go back to Vivado and open the hardware manager. Open target -- open new target -- add the virtual cable twice, and there we go. Use program device from the lower left (under open hardware manager), and the FPGA bitstream is loaded.

    Now back to the SDK. Select "sumatra" from the hierarchy list on the left. Right click for menu. Run as -- Launch on hardware.

    It works!

    The red LED is blinking.

    Make a change, add verilog to blink the green LED

    What I want to do now is to divide down the 50 Mhz fabric clock and use it to blink the green LED. Let's modify this working project to do that.

    Back to Vivado. Get rid of Hardware manager by clicking the "X" at the upper right.

    I use add sources to add blinker.v. Double click on this to bring it up for edit. I copy and paste into the vivado edit window and make a few edits. This will take "clock" as input and yield "led" as output. I add this to the block diagram. I attach its input to FCLK_CLK0 (which is connected to many other things on the diagram). I make its output external and rename it "green_led".

    I click the "generate bitstream" 2x2 matrix icon at the top of Vivado. It saves my project and asks nicely about regenerating, then away it goes. Finally we get a bitstream.

    I launch hardware manager, then go to open target and notice a new entry in the next menu "recent targets". I select that and am back on the air without extra fuss.

    It works!

    The red LED is blinking at 1 Hz and the green light at 2 Hz.

    And I see proper messages at 115200 baud over the serial port.

    Here is the verilog for blinker.v that runs on the FPGA (PL):

    module blinker (
        input clock,
        output led
        );
    
    parameter p_CNT_xHZ = 12_500_000;
    
    reg [99:0] r_CNT = 0;
    
    reg  r_TOGGLE = 1'b0;
    
    always @ (posedge clock)
        begin
          if (r_CNT == p_CNT_xHZ-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
    
    And here is the C code for blinker.c that runs on the ARM (the PS):
    #include 
    #include "platform.h"
    #include "xil_printf.h"
    #include "xgpio.h"
    #include "sleep.h"
    
    int main()
    {
        XGpio led_gpio;;
        int state = 0;
    
        init_platform();
    
        print("Here we go ....\n\r");
    
        XGpio_Initialize ( &led_gpio, XPAR_AXI_GPIO_0_DEVICE_ID);
        XGpio_SetDataDirection ( &led_gpio,1,0);
    
        for ( ;; ) {
    	usleep ( 500000 );
    	state = ! state;
    	XGpio_DiscreteWrite ( &led_gpio, 1, state);
    	print ( "beep\n\r" );
        }
    
        cleanup_platform();
        return 0;
    }
    
    // THE END
    

    Feedback? Questions? Drop me a line!

    Tom's Computer Info / tom@mmto.org