Using and Modifying User Constraint (UCF) Files for Fun and Profit

So, you've finished simulating your processor, and now you want to put it on the board. You've messed around with asm_to_hex.pl, named all your wires, and stared blankly at a waveform for hours before muttering a noncommittal "good enough." Now it's time to grab a cup of coffee and wait for things to either resolve themselves into digital logic or explode in flames.

Without guidance, the implementation tool will randomly bond your inputs and outputs to pins on the FPGA. This is, suffice it to say, not a very good situation. It's smart enough not to catch the FPGA on fire, but not smart enough to do anything particularly useful. This is where the User Constraint File steps in, here to save the day, like a really boring superhero.

There are four main categories of components that you might be interested in using: clocks, pushbuttons, DIP switches (all inputs), and the LEDs (output). CSE378.ucf is the standard UCF file we'll be using, and it contains all of these that you could possibly want.

Debugging Interface

To use the logic analyzer, you need to add the following output ports to your design:

The two ground ports MUST BE CONNECTED TO GND!.  The LA_TRIG port should be connected to CLK.  The other two ports (LA_A0, LA_A1) are for your own use.  To connect these on the board, download the CSE378.ucf file and be sure to remove the comments from lines at the end with LA_* in the name.

On the logic Analyzer, the setup file is in My Documents/Left Digilent Expansion Header Cable.tla.  This assigns the name opcode for LA_A0(7:2) and PC for LA_A1(7:0).  You don't need to actually connect these signals to these pins. This is just the default names for the output waveforms.

Clocks

OK, you might not have a particular desire to clock the design, but you're going to have to, anyway. The clock we'll be using for the labs is the 100 MHz system clock on the XUP board. In the default cse378.ucf, it's named "SYSTEM_CLOCK". There is a certain formula which will be reused for every signal we'll deal with: SYSTEM_CLOCK requires (as do the other clocks) a couple extra steps. See that line about TNM_NET? Change both mentions of SYSTEM_CLOCK to match your own net. In addition, change the timespec "TS_SYSTEM_CLOCK" to read timespec "TS_FOO", where FOO is your net, and change the "SYSTEM_CLOCK" later in the line to match as well.

Example snippet:

(the 100MHz system clock is mapped to net CLK in the top-level design.)

# NET "MGT_CLK_P" LOC = "F16";
# NET "MGT_CLK_N" LOC = "G16";
# NET "EXTERNAL_CLOCK_P" LOC = "G15";
# NET "EXTERNAL_CLOCK_N" LOC = "F15";

NET "CLK" LOC = "AJ15";
# NET "FPGA_SYSTEMACE_CLOCK" LOC = "AH15";
# NET "ALTERNATE_CLOCK" LOC = "AH16";

# NET "MGT_CLK_P" IOSTANDARD = LVDS_25;
# NET "MGT_CLK_N" IOSTANDARD = LVDS_25;
# NET "EXTERNAL_CLOCK_P" IOSTANDARD = LVDS_25;
# NET "EXTERNAL_CLOCK_N" IOSTANDARD = LVDS_25;

NET "CLK" IOSTANDARD = LVCMOS25;
# NET "FPGA_SYSTEMACE_CLOCK" IOSTANDARD = LVCMOS25;
# NET "ALTERNATE_CLOCK" IOSTANDARD = LVCMOS25;

NET "CLK" TNM_NET = "CLK";
TIMESPEC "TS_CLK" = PERIOD "CLK" 10.00 ns HIGH 50 %;
Now that that's done, more fun is to be had with the other three categories.

Pushbuttons

There are five pushbuttons on the XUP board: Up, Down, Left, Right, and Enter. They're all active-low, so if you use them for active-high inputs, they will need to be inverted before being fed into your design. In addition, they're not debounced -- there are capacitors and pull-up/pull-down resistor components in the Virtex-II Pro Xilinx libraries to be had if you've a jones for debouncing.

In any case, these map to PB_UP, PB_DOWN, PB_LEFT, PB_RIGHT, and PB_ENTER lines in the UCF file.

Example snippet:

(the "enter" pushbutton is routed to net RESET in the top-level design.)

NET "RESET" LOC = "AG5";
# NET "PB_UP" LOC = "AH4";
# NET "PB_DOWN" LOC = "AG3";
# NET "PB_LEFT" LOC = "AH1";
# NET "PB_RIGHT" LOC = "AH2";

NET "RESET" IOSTANDARD = LVTTL;
# NET "PB_UP" IOSTANDARD = LVTTL;
# NET "PB_DOWN" IOSTANDARD = LVTTL;
# NET "PB_LEFT" IOSTANDARD = LVTTL;
# NET "PB_RIGHT" IOSTANDARD = LVTTL;

DIP Switches

These are little tiny two-position switches that are really annoying to frob if you've got large fingers (like the author.) They are active-low as well, so moving them to the ON position (as marked on the block) will yield a logical 0. Note that the only DIP switch block we have any control over is the one under the lowest cutout, near the pushbuttons and LEDs.

These map to SW_0 through SW_3, with SW_3 on the left-hand side.

Example snippet:

(the rightmost DIP switch is routed to net mode in the top-level design.)

NET "mode" LOC = "AC11";
# NET "SW_1" LOC = "AD11";
# NET "SW_2" LOC = "AF8";
# NET "SW_3" LOC = "AF9";

NET "mode" IOSTANDARD = LVCMOS25;
# NET "SW_1" IOSTANDARD = LVCMOS25;
# NET "SW_2" IOSTANDARD = LVCMOS25;
# NET "SW_3" IOSTANDARD = LVCMOS25;

LEDs

Unfortunately, our boards are very much blinkenlight-deficient. We have only a nybble of LED capacity controllable by the user. Sadder still, there are no fewer than six board-controlled lights, three of which are always off, one of which is always green, and one of which is always blinking red unless you're Adrian. Such is life.

The user-controllable LEDs, as with the DIP switches, are numbered from right to left, and are active-low. They map to LED_0 through LED_3.

Example snippet:

(the LEDs are wired to display the low nybble of PC / 4. note that this is an incredibly bad idea without a single-step feature, as PC changes too fast to be visible on the LEDs.)

NET "PC[2]" LOC = "AC4";
NET "PC[3]" LOC = "AC3";
NET "PC[4]" LOC = "AA6";
NET "PC[5]" LOC = "AA5";   

NET "PC[2]" IOSTANDARD = LVTTL;
NET "PC[3]" IOSTANDARD = LVTTL;
NET "PC[4]" IOSTANDARD = LVTTL;
NET "PC[5]" IOSTANDARD = LVTTL;

NET "PC[2]" DRIVE = 12;
NET "PC[3]" DRIVE = 12;
NET "PC[4]" DRIVE = 12;
NET "PC[5]" DRIVE = 12;

NET "PC[2]" SLEW = SLOW;
NET "PC[3]" SLEW = SLOW;
NET "PC[4]" SLEW = SLOW;
NET "PC[5]" SLEW = SLOW;