miniSpartan3 is a smaller board, with less features and a Spartan3 Xilinx FPGA instead of the newer generation Spartan6. However, it is very competitively priced, with the board I received costing only $39 – which is a bargain for a small dev board with HDMI out, really.
I thought I’d write a small post about ho to get this board set up and running some “hello world” test. To do this, we need a few things:
- Xilinx ISE 14 WebPack
- A HDL design to run
- A UCF constraints file for the miniSpartan3 board
- xc3sprog to program the board
Lets get started!
This is the official Xilinx development environment for the Spartan3 FPGA family. It has a free version, but is a significant download – you can find it here. The instructions on my “Designing a CPU in VHDL” part are still correct, so you can follow them to obtain the installer.
It’s worth noting that ISE has problems on Windows 10 systems. It installs, but crashes when open file dialogs present. There is a patched set of DLLs which aim to solve this, and a few videos you can find on youtube explaining the steps. I’ve used these and it seems to run fine now.
On starting the ISE Project Navigator (I run the 64-bit version) you can start a new project. Throw in a name, location, and optional description. Our top-level source is HDL.
Now we must specify the settings for the project. The miniSpartan3 board is (unsurprisingly) a Spartan3A family FPGA part. Specifically my board is the XC3S200A part, in the VQ100 package. If you are following me in VHDL, set that as your preferred language and progress to the next screen.
The next screen is simply a summary. Ensure here the device and package is correct 🙂
Once the project is created we are presented with an empty project hierarchy. We shall add some new source to it.
The source we want is a VHDL module. I’ve called mine led_top, as it is going to use the LEDs and is out top-level module.
The module will have 2 inputs and one output port. The inputs are the clock, and our two on-board switches. The output is the three LEDs present on the miniSpartan3 board. We can use the new source wizard to define the interface boilerplate for us – the 3 LEDs would be defined as an output bus, with 3 bits – MSB 2 down to LSB 0. Completing the wizard creates source for us and we can then add our additional logic to it.
Our Hello World project
The end goal for our hello world project is simple:
- One switch will enable our LED counter output, or fix them all as lit
- The second switch will select a fast binary count, or slow binary count
The LEDs will count in binary from 0 to 7. Internally, there will be a 32-bit counter, which increments every cycle of our input 32MHz clock. The switch will simply select a different sub-range of bits to view into this 32-bit number. Due to it incrementing every cycle, the windows will be in the high bit range, say bits 24, 25 and 26. This should allow you to see the LEDs move, instead of them being a blur.
To achieve this, we need to introduce our counter. It is a signal within the behavioral led_top architecture definition.
architecture Behavioral of led_top is signal count: unsigned(31 downto 0) := X"00000000"; ...snip...
This count signal is of type unsigned, which is compatible with the STD_LOGIC_VECTOR type of our LED output. Unlike STD_LOGIC_VECTOR, it has arithmetic operations defined – which you can use in projects by uncommenting the arithmetic package line near the top of the autogenerated boilerplate.
-- Uncomment the following library declaration if using -- arithmetic functions with Signed or Unsigned values use IEEE.NUMERIC_STD.ALL;
The LEDs are a vector of 3 std_logic bits. We will also define a signal to hold their state, which will capture the relevant values of bits in the counter each cycle.
signal threeBits: std_logic_vector(2 downto 0) := "000";
We then define a process block, which is ‘run’ every time the state of I_clk changes.
process(I_clk) begin if rising_edge(I_clk) then count <= count + 1; if I_switches(1) = '1' then threeBits <= std_logic_vector(count(25 downto 23)); else threeBits <= std_logic_vector(count(28 downto 26)); end if; end if; end process;
Following this code, each rising edge of the input clock cycle, we will increment the unsigned count signal by 1. Then, depending on the state of our second switch, we will capture three bits of data from that counter, and place them in our ‘threeBits’ signal.
Outside of a process, we have an asynchronous assign to the output LEDs.
O_leds <= threeBits when I_switches(0) = '1' else "111";
This will output the three bits of our counter to the LEDs when our first switch is active, otherwise output a state in which all LEDs are on.
The full source for this small sample, for clarity, is below.
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; entity led_top is Port ( I_clk : in STD_LOGIC; O_leds : out STD_LOGIC_VECTOR (2 downto 0); I_switches : in STD_LOGIC_VECTOR (1 downto 0)); end led_top; architecture Behavioral of led_top is signal count: unsigned(31 downto 0) := X"00000000"; signal threeBits: std_logic_vector(2 downto 0) := "000"; begin process(I_clk) begin if rising_edge(I_clk) then count <= count + 1; if I_switches(1) = '1' then threeBits <= std_logic_vector(count(25 downto 23)); else threeBits <= std_logic_vector(count(28 downto 26)); end if; end if; end process; O_leds <= threeBits when I_switches(0) = '1' else "111"; end Behavioral;
Now that we have a top-level VHDL module for the behavior we require, we need to now ensure those inputs and outputs are mapped to the correct pins on the miniSpartan3 board.
Creating the constraints file
The constraints file contains mappings from net names to pins on our board, along with relevant standards for what kind of I/O is used. We create a User Constraints File (.ucf) using the New Source option as previously used for our VHDL module.
We need to define mappings for our I_clk, I_switches and O_leds nets to correct pins on the FPGA package. For that, we need to inspect the schematic of the miniSpartan3 board, which is provided on the github project from Scarab Hardware.
We can see from the schematic that our LEDS, switches and clock input are clearly defined. The UCFfile is a list of NET names to LOC pin locations, along with modifiers. For example, we can see the I_clk input is on pin P85. So we define that as so:
# 32 MHz clock NET "I_clk" LOC = "P85";
We can also follow the lines from the LEDs to the FPGA inputs, and get their mappings.
# Leds NET "O_leds<2>" LOC = "P16"; NET "O_leds<1>" LOC = "P19"; NET "O_leds<0>" LOC = "P20";
When it comes to the switches however, we need to give some more information. If you look at the schematic, you can see the switches connect directly from the fpga, though the switch, to ground. This means that we need the input to be ‘pulled up’ via an internal resister, to the high logic level. This basically means that unless the input pin is forced to ground, it will be pulled high. In this case, we state thee net is also PULLUP.
# Switches NET "I_switches<0>" LOC = "P98" | PULLUP; NET "I_switches<1>" LOC = "P99" | PULLUP;
with those 6 NET definitions in our UCF file, we can now go ahead and create a programming file.
This will create a led_top.bit file in our project folder. We will flash this file onto the miniSpartan3 board.
Flashing the miniSpartan3
Normally you flash a small SPI memory chip which FPGAs read from on power-up, but the easiest way to get your design working is to just flash the FPGA on a 1 time basis. This means you need to re-flash each power cycle and you cannot reset it, but is fine for this Hello World example. To do this, you can use a program called xc3sprog. More information is available here.
“xc3sprog is a suite of utilities for programming Xilinx FPGAs, CPLDs, and EEPROMs with the Xilinx Parallel Cable and other JTAG adapters under Linux.”
Despite the blurb above, it also works under Windows, which is my primary platform just now. You can find a built windows binary on sourceforge.
xc3sprog -c ftdi led_top.bit
One-time flashes the device, and we get a working Hello World example! Note there is a failure message there, but it works.