Exploring the Arrow SoCKit Part I - Blinking LEDs
In September, I bought one of the new Arrow SoCKits. They are development boards for the Altera Cyclone V, a system-on-chip with an ARM processor and FPGA. Now that I’m out of school, I finally have some time to play around with it. I’ve decided to document the things I do with the SoCKit as a series of tutorials. Figuring out how to work with FPGA boards can be confusing and requires poring through a bunch of vendor datasheets and example code, so hopefully someone finds these tutorials helpful. For these tutorials, I will assume that you have programmed before and have some rudimentary understanding of digital electronics. If you have never programmed before, there are literally tons of free resources online. To be honest, though, FPGA programming and embedded systems might be a bit too challenging for first-time programmers, so I’d recommend getting more programming experience before coming back to these articles. If you have never studied digital electronics, ASIC World has a pretty good tutorial. I will be using the Verilog and SystemVerilog hardware description languages for these tutorials. I don’t expect you to be familiar with either. I will explain the syntax as I go along. All of the hardware descriptions can be found on Github.
The first thing to do when trying out a new FPGA dev board is to get the LEDs to blink back-and-forth, Knight Rider style. This is a pretty simple circuit to create and gets you familiar with using the design tools and programming the chip. To make things more interesting, let’s also design our circuit so that the speed at which the LEDs sweep back-and-forth can be controlled using the push-buttons on the board.
Installing the Software
For programming the FPGA, you will need Altera’s Quartus II Web Edition design software. There are versions for Windows and Linux. Altera officially supports only Red Hat Enterprise Linux with their Linux version, but you can install it on other distributions. If you are using Arch Linux like I am, there is a pretty good article on how to install it on the Arch Wiki. Those instructions may also be applicable to other distros. When downloading the files for installation, do not download the “Combined Files” package. It is 4.5 GB and contains some device families that you will not need. Instead, go to the “Individual Files” section and download “Quartus II Software”, “ModelSim-Altera Edition”, and “Cyclone V device support”. This should give you files named like “QuartusSetupWeb-w.x.y.z.run”, “ModelSimSetup-w.x.y.z.run”, and “cyclonev-w.x.y.z.qdz”. Change the .run files to be executable and then run the “QuartusSetupWeb-w.x.y.z.run” file. Follow the installation instructions given.
Creating the Project
Start Quartus and click on the big button labeled “New Project Wizard”. On the first screen, enter the directory you’d like to put the project files in (you’ll probably want to create a new directory for this). Give your project a name (I called mine “sockit_test”). Skip page 2, as you have no files to add. On page 3, set the device family to Cyclone V and choose 5CSXFC6D6F31C8 as the specific device. On page 4, pick ModelSim-Altera as the Simulation tools and choose “SystemVerilog HDL” as the format. This is not important for this part, since you won’t be doing simulation just yet, but it will come in handy later. Once you get to page 5, you can press Finish.
Top-Level File and Pin Assignment
Now that you’ve created the project, you can set up the top-level file and assign the pins you need to it. In Quartus, create a Verilog file by clicking “File” -> “New” -> “Verilog HDL file”. Save the file with the same name as your project (so if you called your project “sockit_test”, save it as “sockit_test.v”. I recommend putting your HDL files in a separate subdirectory in your project folder called “rtl”.
Put the following Verilog code in “sockit_test.v”.
If you’re not familiar with Verilog, a “module” is a hardware block. In this code, we are simply specifying what the inputs and outputs of the block are. For the top-level module, the inputs and outputs are the pins of the FPGA. For our example, we only need the 4 push button keys, 4 LEDs, and the 50 MHz clock. You can assign the pins on the FPGA to your inputs and outputs by going to “Assignments” -> “Pin Planner” and entering in the following assignments.
Controlling the LEDs
Now that the pins have been assigned, you can start putting together the different modules to make the circuit work. The first module we will make is the one driving the LEDs. We will call it “blinker.v”.
Warning! To everyone reading this who is primarily a "software person" and does not have much experience with digital logic, take heed that though Verilog looks superficially like software code with its "case" and "if" statements, it is actually describing hardware blocks. One particular difference is that there is no concept of order in Verilog. Statements on subsequent lines are all "running" at the same time. To give an "ordering" to computation, you must explicitly design state machines as the previous code does. If you don't keep these things in mind, you might end up writing completely valid Verilog that is impossible to synthesize.
And now back to our regularly scheduled blog post ...
The first part of this module should look familiar to you. I am stating that
this module takes as input a clock
clk, a four-bit
(we treat it as a 4-bit unsigned integer), a
reset signal, and a
pause signals correspond to two of the push buttons
on the board. The output is the 4-bit led output. I have declared this as
which stands for register. Verilog requires you to declare as
that could hold state. It turns out that the output won’t actually hold any
state, but the Verilog compiler is not clever enough to figure this out.
If this confuses you, don’t worry. It will make more sense once I explain
The second part of the module are some internal registers
running, which are initialized to 0, 0, and 1, respectively. These registers
actually will hold state, unlike the output
The third part of the module are the
always blocks. These constructs tell
our hardware to perform some operation whenever the signals in the sensitivity
list (the stuff inside the parenthesis after the
@ sign) change.
In the first
always block, the sensitivity list is the signal
so the operations inside the
always block will occur whenever
always block is a
case statement that maps certain values of
pos to certain values of
led. You will notice that in everything except the
default case, exactly one bit in
led is high, corresponding to a lit led.
pos, the lit led goes to the left and then back to the right.
Since every possible case of pos has been covered, this
always block functions
as a combinational circuit. If we had left out a case (say, by getting rid of
the default case), the compiler would warn us about inferring a latch. That is,
pos happened to be in a case where the behavior was unspecified, the
led would keeps its previous value. You can see now why
to be declared a register even though it isn’t one.
always block contains in its sensitivity list,
This means that the
always block is triggered by the rising edge of
always block is a large nested if statement. Here, we state what
values each of the internal registers will take at each cycle. If
triggered, we change
running back to their original values.
pause is triggered, we toggle the value of
running. If we are running,
then we are under normal operation, during which we want to regularly increment
pos until it reaches 5, at which point we wrap around back to 0. However, we
want the incrementing of
pos to happen slowly at a controlled speed.
We accomplish by initially setting the
count variable to the value of
multiplied by 220, decrementing until it reaches 0, and then
resetting it. The value of
pos is then only updated when
delay is reset.
There is no logic specified for when
running is false, so in that case all
Setting the delay
So how do we set the
delay variable that the
blinker module needs?
We’ll have to create a different module. Call it “delay_ctrl.v”.
This module takes as input the clock, two control signals
as well as a
reset signal. The control signals correspond to push buttons.
The output is the 4-bit
delay which will feed into the blinker.
We declare an internal register
delay_intern and initialize it to 8,
which is the halfway point. This internal register is then assigned to
delay output. In our positive-edge triggered
always block, we first
check to see if a reset is triggered, in which case we set
to 8. If
faster is pressed, we reduce the delay. If
slower is pressed we
increase it. If none of the control signals are high, we maintain state.
Handling the Buttons
In our previous two modules, we assumed that our control signals would be high for exactly one cycle after the keys are pressed. Given the speed of the human finger, this would obviously be impossible if the control signals were tied directly to the keys. Therefore, we need a unit to detect when each key is pressed and set the corresponding control signal high for one cycle. We will call it “oneshot.v”.
edge_sig is the input from our keys and
level_sig is the output
for our control signals. The trick here is that we keep two 4-bit registers
last_value. On each cycle, we read the values of the keys
cur_value and the previous value of
The signals from the keys are 0 when pressed and 1 when unpressed, so we
want each bit of our output to be high when the current value is 0 and the
last value was 1, which is what the
assign statement is doing.
You may think that I have mixed up the order of
always block. But actually, order does not matter when using the
<= assignment operator. When using
<=, the values being read
will always be the values from the previous clock cycle.
Tying it All Together
Finally, we must tie our three components together in our top-level module.
We’ve now expanded our original “sockit_test.v” to connect everything together.
We have three internal signals,
main_clk. These signals
wire, which is the opposite of
reg. Our modules are tied to
these signals using “port mappings”, which are statements of the following form.
module_name is the name we gave to the module, and
instance_name is the
name we give to this instance of the module. The instance name doesn’t really
matter as long as they are unique within a module. The
is just the input/output name given inside the port-mapped module.
external_signal name of the wire in the outer module.
Compiling and Programming
Now that we’ve finished our circuit, we can compile our hardware description and program it onto the FPGA. First, though, let’s do a quick static check to make sure we didn’t screw up somewhere. Run “Analysis and Synthesis” by clicking on the icon with a purple triangle and blue check mark in the tool bar (third from left in below image). Wait for the action to complete. You can watch its progress in the “Tasks” window at the middle left. After it’s finished, inspect the log output in the “Messages” window at the bottom. Make sure it doesn’t have any errors or warnings beyond “Parallel compilation is not licensed and has been disabled”. If you do see warnings or errors, look back at your hardware descriptions and make sure there isn’t a typo. “Analysis and Synthesis” will be useful later on for catching syntax mistakes.
Now that you’ve checked the descriptions, you can run a full compilation by clicking the icon with the purple triangle (second from left in the image). Be prepared to wait a little while. Once the compilation is complete, a file called “sockit_test.sof” should be generated in the “output_files” subdirectory of your project folder. This file contains the configuration you will program into the FPGA.
Before you try to program the FPGA, make sure that the USB Blaster drivers are installed correctly (check the Arch Wiki article at the top for Linux, or this article for Windows). Make sure you have the SoCKit connected to your computer correctly. The USB Blaster port is the microUSB port farthest to the right if the ports are facing toward you.
Careful! Early versions of the SoCKit board had surface-mounted microUSB ports with no reinforcement. The microUSB ports on these boards are likely to break off if you push too hard when inserting the USB cable. Later versions of the board came with a reinforcing metal plate to fix this problem. If you have one of the earlier boards, be very careful when plugging in the microUSB cable.
Once you are sure the drivers are working, open up the programmer by double-clicking on “Program Device” in the “Tasks” window, by clicking on “Tools” -> “Programmer” in the menu, or the Programmer button in the toolbar (second from right in the above image). In the new window, go to “Hardware Setup” and make sure “Currently selected hardware” is set to something like “CV SoCKit”. If you cannot find this selection in the dropdown menu, you may want to check that the board is on, the USB blaster is connected, and the drivers are installed properly. Once you’ve selected the correct hardware, press the “Auto Detect” button in the programmer window. It may ask you to choose your device. Choose “5CSXFC6D6”.
You should now see two devices, 5CSXFC6D6F31 and SOCVHPS. The former is the FPGA, the latter is the ARM processor. Right click on the entry for the FPGA and select “Change File”. Pick the “sockit_test.sof” file that was generated during compilation. Now press start, and the .sof file will be programmed onto the FPGA. If you are successful, the SoCKit will look like the following.
Congratulations, you just programmed an FPGA! In my next post, I will take a look at the ARM processor on the Cyclone V and how to install an operating system on it.