Update: Just noticed, the Digilent Arty Reference Manual does not include the package pin mapping for the Arty board’s ChipKit header (the Arduino-looking one). You can find them in the constraints file here.
This post documents the process needed to program a Digilent Arty board, using the Vivado 2017.3 software with a Verilog “hello world” project. Unfortunately, the Vivado software is confusing at best, and the 2017 version contains a few key differences from the last guide I could find (a 2015 version – eb.dy.fi/2015/11/arty-hello-world/), which had me stuck for a while.
Firstly, you’ll need to download the Vivado software from the Xilinx website. The WebPack version suffices for this device. Once this is done, install the Board Support files from Digilent, which you can find here. Unzip the file, and copy zipfile\vivado-boards-master\new\board_files\* to vivado_path\data\boards\board_files.
Note that on purchasing an Arty kit from Digilent, you’ll be provided with a slip of paper with a license code for the Vivado software. This (to my understanding) provides you with support for the Design Edition of this software, for one year, locked to the device you’ve purchased – it is not necessary to make use of this at any point.
Creating a Project
Now, open Vivado, and click “Create Project”. Select the “RTL Project” option, and tick the “Do not specify sources at this time” box for simplicity’s sake. Keep clicking through the application until you get to select a “Default Part”, and click “Boards”:
You should see the “Arty” board – select this, and hit “Finish”.
Creating a Module
Our next step is to create our top module. In the Flow Manager (the box on the left), click “Add Sources”, and select “Add or create design sources” when prompted. In the next window, click “Create File”. You should see something like this:
Leave this as Verilog for now, and name it what you want. Hit OK to create the file, then hit Finish. A new dialog box should pop up, allowing you to specify the IO parameters of the file. We want to define some switches for input, and some LED’s for output, as follows:
You may be thinking, “where do I assign IO ports” – don’t worry, we’ll come back to this later – just hit OK for now. Let’s create a very simple Verilog hello world example:
module top1( output [3:0] leds, input [3:0] switches ); reg [3:0] led_status; assign leds = led_status; always @(*) begin led_status = switches; end endmodule
Once this is entered, save your work, and use the Run Synthesis function on the left (or press F11). A box should pop up, asking you where to save the results – just leave it for now, this will save to the project directory you created right at the start. Once done, select the “Open Synthesized Design” option. You should see something like this:
Mapping IO Ports
Our next task is to tell the software which IO pins map to which ports. Go to the Window menu, and click the “I/O Ports” item. A new tab should appear in the status section at the bottom, and gain focus. It should look like this:
Here, we can define what the “leds” and “switches” actually map to on the board. Here, we can refer to the Arty reference guide (shamelessly copied from http://eb.dy.fi/2015/11/arty-hello-world/):
You can download a more full-featured reference from the Digilent website here. It explains how to interact with the rest of the board.
Let’s map the buttons and the LED’s to their respective pins, and operate them at 3.3v, as follows:
Once this is done, hit “Run Implementation” in the Flow Manager box. When prompted, save the constraints file to whatever name you like.
Note that this process of adding constraints will make our synthesis outdated – when prompted, simply re-run the synthesis process. If all goes well, you should see the following dialog box:
At this point, you can generate the bit stream, and we will proceed to program the device.
Programming the Device
At this point, connect your device to your computer and open the Hardware Manager
(last option in the Flow Manager box). Select “Open Target”, then “Auto Connect”. The “Program Device” button should now be enabled. On clicking this, you’ll be prompted for a bit stream file.
Be careful at this step – this “Open File” dialog box will default to the last place you used it. This may be your last project, and if all your file names are “fuck.v” or “lol.v”, this can result in some sadness.
Select your bit stream file – this will be in projectdir/runs/impl_1 – then hit Program. Your LED’s should go off, but will light up once you hit the corresponding buttons.
Using a Clock Generator
One major change from the IceStorm development environment is the presence of the Clocking Wizard. This generates wrapper code to turn an on-board 100Mhz clock into whatever clock output you want – the maximum (according to Xilinx) is 1.6Ghz. It is possible to tap into this clock signal directly via pin E3, but it’s possible to use the Clocking Wizard IP to make a new clock, as follows.
To add a clock, in the Flow Manager window, click the “IP Catalog” icon. Search for and select the “Clocking Wizard” – you should see something like this:
On double clicking, you’ll see the clock configuration window, as follows (for me, the “Show disabled ports” was ticked by default – but this is irrelevant for now):
In this example, we’ll be using the system 100Mhz clock to drive the pin. Set the CLK_IN1 input to “sys clock”.
At this point, you may be tempted to link the “EXT_RESET_IN” input to “reset”: but don’t. If you do, the clock will be enabled when the reset is disabled (i.e. pressed), and you ‘ll need to hold down the reset button on the board to operate the clock.
Next, we’ll go to the “Output Clocks” option. Here, change the Requested output frequency to what you want:
Then, hit OK. You should see something like this:
Here, leave everything and hit Generate. This won’t actually tie up any in your design, but will generate instantiation code, which lets you “use” the clock: that is, this step generates a Verilog function which you can use to feed in an actual clock. To access this function, navigate back to the Sources view, and expand the new node in the source tree (by default, clk_wiz_0). Open the new Verilog file, and you should see this:
Copy the code at the bottom – think of this as a black-box module you can now use. Now, instantiate this in your code as follows:
module clktest( input clk_in1, input reset, output [3:0] leds ); wire lock_led; reg [32:1] q; reg [3:0] leds_reg; wire clk_out1; clk_wiz_0 instance_name ( .clk_out1(clk_out1), .reset(~reset), .locked(lock_led), .clk_in1(clk_in1)); assign leds = leds_reg; initial begin q = 0; leds_reg = 4'b0; end always @(posedge clk_out1) begin q <= q + 1; if (q == 1'b1) begin leds_reg <= leds_reg + q; q <= 32'b0; end end endmodule
Now, follow the above guide to create a bitstream and program your device, and you should see blink.v come to life.
Simulating a Design
It is also possible to use the software to simulate a design. In fairness – this is a big improvement over the Icarus Verilog / GTKWave workflow for the IceStorm environment. To set up a simulation, right click “Simulation Sources” in the Sources window, and add a new simulation source file. In this example, we can use the following simple simulation logic:
module sim_1( ); reg [3:0] buttons; wire [3:0] e_leds; top1 test_target (e_leds,buttons); initial begin #50 buttons <= 4'b1010; #100 buttons <= 4'b1011; end endmodule
This will cause a pattern of “1010” to be fed to the buttons at every 50 intervals, then a pattern of “1011” to be used at every 100 intervals. Then, click “Run Simulation” in the Flow Manager area, and click “Run Behavioural Simulation”. You should see something like this:
Note that simulating a clock is a little different – while it is possible to right click a clock input signal and use the “Force Clock” option, it may be more logical to write a test bench which simply flips the clock quickly (you can feed this into a PLL / clockgen):
module clktest_tb( ); reg clk_in1_t; wire reset_t; wire [3:0] leds_t; initial begin clk_in1_t = 1'b1; end clktest c (clk_in1_t, reset_t, leds_t); always @(*) begin #1 clk_in1_t <= ~clk_in1_t; end endmodule
I hope this post helps you get started with this device. I’m learning this as I go, so if I’ve made a mistake, or designed something in an ineffective manner, please let me know via the comments section 🙂