Hecatronic
1329 words
7 minutes
Remotely Flashing a Xilinx FPGA

Something I’ve been looking at recently is the use of FPGA’s or Field Programable Gate Arrays. These devices can be programmed to layout custom logic gates allowing the virtual design of custom CPU’s or digital hardware such as ethernet controllers. Another use is the ability to create virtual hardware that’s laid out in such a way to re-create old retro hardware such as Spectrum’s or Comodore 64’s

For retro hardware the main project for this is the Mister project,
however that’s deisgned for use on the DE10 board, in my case I’m using the Xilinx Art-A7 100T

My end goal is to try out amaranth as a python based HDL (Hardware Description Language) which transpiles down to Verilog.
But as a first step before that is to get a very basic Xilinx project working and see if I can remotely program a board connected to an RPI

I’ve placed a download here for the example below

Setting up a Demo Vivado Project#

First lets setup a very basic Xilinx based project to blink an LED

Setting up a new Project#

  • Launch the Xilinx Vivado IDE
  • File -> Project -> New
  • Project Name: xilinx-example1
  • Directory: Choose a directory to place the project into
  • Select RTL Project
  • Tick the box for “Do not specify Sources at this time”
  • On the next screen select the Boards Tab
  • Click Refresh, this will download the latest list of available boards
  • Filter the list to the 100t board
  • Click the Download button

We should now have an empty project with no sources targeted at the Arty A7-100T board

Adding System Verilog Source#

Next we’re going to add a basic design to blink an LED within System Verilog

  • Select Add Source
  • Select the option for Add or Create design sources
  • Select Create File
  • Select System Verilog
  • Give it a filename of led_blink1
    This should automatically add an extension of .sv
  • You might also have to right click the file and “Set as Top”
  • The new file will now show up under Design Sources
  • Open up the file then paste in the below code
  • Then click Save
module led_blinker (
    input  logic clk,   // 100 MHz clock input
    output logic led = 0    // LED output
);

    logic [26:0] counter = 0;

    // Counter process: counts the clock cycles
    always_ff @(posedge clk) begin
        counter <= counter +1'b1;
        // Set to 10 for simulation, 100_000_000 for live
        if (counter == 10) begin
            counter <= '0;       // Reset the counter after reaching MAX_COUNT
            led <= ~led;    // Toggle the LED every 1 second
        end
    end

endmodule

We’ve set the counter limit above to 10 to get a better view during the simulation.
When flashing the actual board we’ll change this to 1000000 for a longer 1 second delay.

We should now have some System Verilog in for flashing an LED.
If using git you may want to add in a .gitignore file to exclude the cache directory.

Setting up a Simulation File#

Next we’re going to add something to simulate the design

  • Select Add Source
  • Select the option for Add or Create simulation sources
  • Select Create File
  • Select System Verilog
  • Give it a filename of tb_led_blink for the test bench
    This should automatically add an extension of .sv
  • The new file will now show under Simulation Sources
  • Open up the file then paste in the below code
  • Then click Save
`timescale 1ns/1ps

module tb_led_blinker();
    // Clock signal for the DUT
    logic clk;
    // LED output from the DUT
    logic led;
        
    // Instantiate the DUT (Device Under Test)
    led_blinker uut (
        .clk(clk),
        .led(led)
    );
    
    // Clock generation: toggles the clock every half period (5ns for 100 MHz)
    always begin
        #5 clk = ~clk;  // Toggle the clock every half period
    end

    // Testbench procedure
    initial begin
        // Initialize signals
        clk = 0;
        $monitor("At Time: %0dns, LED: %b", $time, led); // Monitor LED State
        #1000005 $finish;
    end
    
endmodule

Running the Simulation#

We can now run the simulation

Click on the zoom out button and full width button.
This should give us a better view of the logic state of the lines.

  • The top trace represents the 100Mhz Clock
  • The bottom trace represents the output to the LED toggling on and off

Simulation Time#

To give a bit of background on the different units of time in use.
The default time length for a simulation is 1000ns (nano seconds).
For those not familiar with engineering scales it tends to work in groups of 3 so

  • 1 ms (milli second) = 0.001 seconds
  • 1 us (micro second) = 0.000,001 seconds
  • 1 ns (nano second) = 0.000,000,001 seconds
  • 1 ps (pico second) = 0.000,000,000,001 seconds

So for 1000ns this is 1 micro second or 0.000,001 seconds. The default clock is 100Mhz or million cycles per second.
If we want to calculate how quickly the clock will change 1 / 100,000,000 = 10 ns for each clock cycle.
The simulation length can be chantged under Tools -> Settings

Setting up a Constraints File#

Next we need to map pins from within the design to IO pins on the board.
The way we do this is by using something called a constraints file, these files tend to have an xdc extension.
The best way to imagine this is a file that maps external IO pins from the FPGA device to variables within the Verilog design.
There’s a list of different files here for different boards

To create the constraints file

  • Select Add Source
  • Select the option for Add or Create constraints
  • Select Create File the same as before
  • Give it a file name of something like design
    The xdc extension will be added automatically
  • Open up the constraints file and paste in the contents from Arty-A7-100-Master.xdc

Most of this will be commented out already, normally we could just uncomment out some of the lines we need.
For this example just copy the following in at the top.
This will import the clock as the clk variable and set the output pin for the led.

set_property -dict { PACKAGE_PIN E3    IOSTANDARD LVCMOS33 } [get_ports { clk }]; #IO_L12P_T1_MRCC_35 Sch=gclk[100]
create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports { clk }];

set_property -dict { PACKAGE_PIN H5    IOSTANDARD LVCMOS33 } [get_ports { led }]; #IO_L24N_T3_35 Sch=led[4]

Next up the counter value from 10 to 1,000,000 within led_blinker.sv

// Set to 10 for simulation, 100_000_000 for live
if (counter == 100_000_000) begin

The final step before we actually program the real board is to click “Generate Bitstream”


Remotely Connecting to the FPGA via an Rpi#

For the next step I’d like to be able to remotely program a board using a Rpi.
In this case I’m using an Rpi4 with a fanless heatsink and with the latest version of ubuntu installed.
Typically with xilinx tools they have something called the hw_server which can be installed onto a remote machine.
However this doesn’t currently work with the AARCH64 / ARM platofrm.

To get around this we can use something called the openFPGALoader this has an option to connect to a board and act as a xvc server.
This won’t be as fast as the hw_server since that uses a newer protocol but the xvc protocol is well documented and useable from the Xilinx tools remotely.

Installing the tools#

First we need to install openfpgaloader

sudo apt-install openfpgaloader

Running the XVC Server#

Next if we plug in the board is should show up as a couple of usb serial ports ttyUSB0 and ttyUSB1.
One of these is the port we can use to program the board, the second is a uart that can be wired into the fpga directly for testing.

To run openFPGALoader in xvc server mode

openFPGALoader -b arty_a7_100t --xvc

This is now showing us that we need to connect to port 3721 for the IP address of the box

Connecting to the XVC Server#

From the Vivado tools we can now connect to the xvc server and upload a bitstream

Select Open Hardware Manager

Select Auto Connect.
At this stage the xilinx will connect to the hw_server on the local machine.

Right click on localhost and select Add Xilinx Virtual Cable.

Enter in the IP address of the Rpi and change the port to 3721.
The same as shown before

This should now show a connection to the board.

We can now select Program Device
and upload the bitstream to the board remotely.

Other Commands#

Some other commands of interest

# If we want to reset the board after it's been programmed
openFPGALoader -b arty_a7_100t -r
# Load the bitstream in via a file
openFPGALoader -b arty_a7_100t bitstream.bit
Remotely Flashing a Xilinx FPGA
https://www.hecatron.com/posts/2024/fpga-1-remote-flash/
Author
Hecatronic
Published at
2024-10-13