Blinky – or flashing the onboard LED is the baseline example used in the ‘starter project’. We encourage all users to start from the starter project, and modify out.
This is because there is a way to brick, or ruin, the device by closing the port needed to program the device in the future. See more about this point here: Enabling SPI
If this is your first time uploading code onto the Fipsy, please follow these directions first. Here we are only describing how the Blinky code works.
Learn about ‘Black Box’ design concepts for an FPGA on the Brief Theory page.
Grab the Blinky code from our GitHub
Specifically, if you have Fipsy Ver 1 – MachXO2-256 then use this link.
If you have the Fipsy Ver 2 – MachX02-1200 then use this link.
Notice there is a project_files folder with a .lpf file you can open (Lattice Project File – in this case FipsyBaseline.lpf) using Lattice Diamond.
Here is the start of the example Verilog code:
Every ‘black box’ is a module, and you declare a module for every set of functionality. The top most module is the one that no other module instantiates, or calls. Here we see how Fipysy_Top is the name of a module, and it is in fact the module that is not called by any other module. It must be declared however. What you see is a list of ports – think wires connecting sections of a motherboard. Ports can be one wire wide, or multiple wires wide.
In the Fipsy starter project, you are using names like PIN7, a variable name, because the example project sets these up for you ahead of time. In fact, the PIN# variables are tied to true numbered physical pins in a configuration file (MakeFPGA.lpf). For your purposes, it’s easier to use these variable names, and know they tie to the same numbered pins on the Fipsy, in the top level module (if you want to use them in lower-level modules, you need to pass them as ports, more on this in later projects).
Here we declare the ports the module uses as input, output, or inout. Data either comes in (to an input port), leaves from the module (as an output port), or can switch states to do both over time.
Now you want to declare wires and registers to be used in the module’s logic. If wires or registers share the same name as the port names declared in the module, they will be physically (electrically) connected. Registers, are persistent 1 or 0 values (current is drawn to voltage, or drawn to ground respectively). Wires are passive, instantaneous state transmitters. There are basically two situations that a wire becomes active: it is wired to an input pin, somewhere along the way, and gets voltage from that, or it is wired to a register (usually via a module’s port), and that drives it high. At no point do you assign a value or state to a wire. It must be wired to something that is driving the state. It can, however, represent the state of some other part of the device, or an input.
The difference between registers and wires is critical knowledge, so look into it.
Registers store values as long as the device has power (and you can change from 1 to 0 or 0 to 1 pretty easily). Most FPGAs lose all state and configuration at power-off, and must be reprogrammed from nearby chips at boot-up. The Mach XO2 inside the Fipsy, however, does keep it’s configuration, but on power-on it will reset it’s state to the stored initial configuration (what you program it to).
Registers, just like ports, can represent one state or a set of states (similar to a list or array in programming). Think of it as, you can have one wire, or you can have multiple wires in a bundle that connects to a port.
Eg.
Means a register named my_var is assigned to a binary value 1 length long (the first 1), with value of 1 (the second 1).
You could also have:
which means a registry called my_vars with a length of 5 binary values, with ordered values 01101. The [4:0] part sets up the storage length, and is similar to saying ‘give me storage from index 4 to index 0’, which is 5 bits long. In Verilog, you have to know the maximum size of the data you are working with ahead of time.
Notice in the block of code for the Blinky example we declare a wire INTERNAL_OSC which stands for internal oscillator. This wire is declared, but it was not part of the module declaration. This is the right part of the code to set up wires and registers your current module uses.
Note that
sets up a wire that is 1 wire wide (one wire). But you could also have something more like a bundle of wires by declaring:
Continuing with the Blinky example, we have:
When it says OSCH OSCH_inst( …); what it’s doing is it’s setting up a module named OSCH and importing its design from the module declaration called OSCH_inst, which is built into Lattice Diamond (included in the project by default, when you set up the chip-type used by the software; a step done for you in the example project files). Inside the parenthesis you take the time to wire elements (registers or wires) from the imported module to the current module.
Basically it’s like this:
You are proactively assigning connections between module ports. Later, when you are synthesizing the design for a device, the Lattice Diamond program (or other synthesizer program) will calculate physical routes to achieve connections you declare.
This is a one-off, block of code that uses Lattice design concepts to establish the internal oscillator. Briefly, 2.08 sets up 2.08MHz as the clock frequency. The frequency here must be one of the supported frequencies listed in the Mach XO2 datasheet (you can take a faster clock-rate and do some logic to break it down into a slower frequency). The line
.OSC(INTERNAL_OSC),
says the module declaration for OSCH_inst has a port named OSC.. to that port connect the wire INTERNAL_OSC, which we declared in the current module.
At a high level, we are setting a variable equal to an expected frequency, and we are getting a wire named INTERNAL_OSC that will pulsate at that given frequency. We can now use this wire as a driver for other logic that must operate on ticks of a clock (the wire can essentially act as microchip clock).
Out2Hz is a wire we set up for later. We have a block of assign PIN=0; with some commented out. The idea is to assign to 0 (ground), any pin we AREN’T using in the project. Pins that are unassigned will float between high and low states unpredictably. It’s just good practice to ground out unused pins, which can help protect the electronics in some circumstances.
You comment out the pins we ARE using, so they can be set to the states referred to in other parts of the module.
This declares another module of type FreqDiv20Bit_inst, which IS part of our code base in the example project. It serves to take a faster clock rate 2.08 MHz, and slow it down to (close to but not exactly) 2Hz.
This module has an input wire named CLOCK which receives the 2.08MHz pulses from INTERNAL_OSC. The code then uses a binary counter, and when the counter reaches a certain threshold, one of the bits is set to 1, and acts to drive an output signal. The threshold is when the Most Significant Bit (in the counter) is one (the MSB is the side of an array of bits with the greatest binary value, for example in binary 3’b100 — here the 1 represents a value of 4, and so the left most bit is the Most Significant Bit). So you have an output port called MSB that connects to a wire named Out2Hz, which is usable in the module as a source for pulses at 2Hz.
Explore the file ‘FreqDiv20Bit.v’ to see how the counter works to generate pulses.
As the comments say, the LED lights up when the value of LEDn is zero, or drawn to ground. So if we want the LED to light up when the value of Out2Hz is high, then we should set it to the ! value, or ‘not operation’ value of of Out2Hz. That is the line
Take a moment to appreciate that we are assigning LEDn here, low in the code, but it is getting it’s state from Out2Hz, higher in the code, which is getting it’s state from the MSB port in another module. Realize that your code represents a design, not a sequence of events. The design processes everything instantaneously, and in parallel. It’s a BIG change in the way of thinking for most programmers. You have to break out of the concept of linear control logic, and think more about connecting wires from here to there, as you would while doing circuit board design.
For debugging and testing purposes, PIN20 is set to Out2Hz, so you can tests that with a multimeter. The clock signal is also tied to PIN11, which is common for electronics, since a lot of times you want one clock to be driving multiple devices, so they are in sync. It’s not really needed, but is there as a demonstration of this concept. The ‘endmodule’ line is important. Technically you can have more than one module per text file, so having proper ‘module’ and ‘endmodule’ nesting is important.
Here is the full code: