Counters in FPGA are particularly cool as you can have as many of them as you like or need, unlike microcontrollers. The other thing is that the counters are all independent of each other in an FPGA. So multiple counters can be used to control things without affecting the operating of anything else...that's very hard to achieve with a microcontroller...the program flow is always linear.
Lets make a counter flash an LED once a second. In order to do that we need to know a couple of things:
What is the clock speed of the oscillator used on the ULX3S development board?
What is the control logic for the LEDS (active high or active low)?
We can find these answers either from the schematic of the ULX3S or from the constraints file. Both of which are helpfully available here:
https://github.com/emard/ulx3s
https://github.com/emard/ulx3s-examples/blob/master/README.md
The schematic is broken down into several pages and was created in KiCad. There is also a PDF version here:
https://github.com/emard/ulx3s/blob/master/doc/schematics.pdf
We are interested in page 4 called 'Blinkey' and page 6 called 'USB'. Blinkey shows the eight LEDS referenced from zero to seven in the centre of the page four in cell B,3. The LEDS are common ground connected which means they are 'active high' - A control signal from 'Bank 7' the FPGA needs to be high in order to get an LED to turn on. The current limiting resistors used are 549 ohms so with 3.3 Vdc logic the current flowing through each LED when on will be 6 mA - bright enough! I've used FPGA development boards in the past where the IO was active low so its useful information to know.
The oscillator is in cell A, 3 on the top middle of the page and it is referenced as being 25 MHz - also useful to know. It is of course possible to use phase lock loops to generate faster clock signals if that is what is required.
The User Constraints File or UCF as it is sometimes referred to is a list of information which tells the software which pins on the FPGA are connected to what and more usefully how they are referred to. We could write our own UCF file if we wanted to and for complicated designs or if we had created our own circuit with an FPGA we would have to write our own. Helpfully the board designer of the ulx3s (EMARD) has written one for us. When we come to write the verilog code we can use the same naming convention and that way we don't have to write our own UCF file.
The sections we are interested in are:
# The clock "usb" and "gpdi" sheet
LOCATE COMP "clk_25mhz" SITE "G2";
IOBUF PORT "clk_25mhz" PULLMODE=NONE IO_TYPE=LVCMOS33;
FREQUENCY PORT "clk_25mhz" 25 MHZ;
and
## LED indicators "blinkey" and "gpio" sheet
LOCATE COMP "led[7]" SITE "H3";
LOCATE COMP "led[6]" SITE "E1";
LOCATE COMP "led[5]" SITE "E2";
LOCATE COMP "led[4]" SITE "D1";
LOCATE COMP "led[3]" SITE "D2";
LOCATE COMP "led[2]" SITE "C1";
LOCATE COMP "led[1]" SITE "C2";
LOCATE COMP "led[0]" SITE "B2";
IOBUF PORT "led[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "led[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "led[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "led[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "led[4]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "led[5]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "led[6]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
IOBUF PORT "led[7]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
Just for reference a hash ( '#' ) symbol before a line means that the text after that is a comment
LOCATE COMP means to locate the drive logic to a particular section within the FPGA fabric.
IOBUF PORT means the line is to be configured as a buffered input or output..
PULLMODE=NONE means the line will not have an internal pull up or pull down resistor enabled.
IO_TYPE=LVCMOS33 means the line will be configured to be low voltage CMOS at 3.3 Vdc.
DRIVE=4 means the line will be able to source 16 mA of current. Drive 1 = 4 mA, Drive 2 = 8 mA, Drive 3 = 12 mA and finally drive 4 - 16 mA
When we write the verilog code we need to refer to the clock as clk_25mhz and the individual leds as led[0]
I was really hoping to use a program called IceStudio to program the ulx3s as it is essentially a graphical front end for apio which collates the usage of Yosys, Ptrellis and NextPNR. Unfortunately the developers of IceStudio do not wish to add the ulx3s to the list of supported development boards. They have their reasons for doing that. I suspect it would be possible to fork IceStudio to work with ULX3S but I'm not going to do that as:
- I don't have the skill!
- I don't have the time to learn the skill!
- I don't want to annoy the IceStudio developers because they are nice guys and have worked hard.
The diagram is meant to show that we have one input called clk_25mhz, we have two outputs called LED and wifi_gpio.
The blue box labelled PrescalerN is some pre-written code which basically is a verilog counter module. It will take the clock input, count how many clock pulses there are and when it has reached 25 000 000 counts it will send a signal to the LED output. The wifi_gpio output will be set high.
In verilog code this looks like this:
module top ( input clk_25mhz, // 25 MHz clock input output reg led [7:0] = 1'b0, // 8 Bit LED Output register, set LED[0] to be in a predefined state output wifi_gpio0 // Output for Wifi enable ); reg [24:0] count = 0; // Register for the counter always @(posedge clk_25mhz) // Interrupt at the positive 25 MHz clock edge begin if (count == 25000000) // If the count register has reached 25 million begin led0 <= ~led0; // toggle the LED[0] On and OFF with a one second interval. count <= 1'b0; // and reset the count to 1 (binary) end else // else begin count <= count + 1'd1; // increment the count by 1 end end assign wifi_gpio0 = 1'b1; //set the wifi_gpio High endmodule
The code should be fairly easy to follow. The wifi_gpio input is only needed if your board has an ESP32. The command enables the ESP32 so code can be pushed over wifi. I haven't been doing that at the moment although I should! I'm fairly certain the code will work, I would like to have tested it and in later posts I will be looking into open source verification software like verilator and GTKwave. These programs allow one to simulate verilog code and show how the inputs and outputs will respond.
Lets save the text file (call it top.v) somewhere sensible I chose: C:\msys64\src\Alex\ulx3s\One Second Counter
Next copy in the constraints file and apio.ini and fire up ConEmu.exe (Windows) and navigate to the folder we just made. It's also at this point that I should state that the board I'm working with has an ECP5 Lattice Semiconductor FPGA so the commands are tailored to it. If you are working with a ECP 12F board or an ECP 85F board you will need to change to commands appropriately.
Type the following command to turn the verilog code in top.v into the bit file ready for uploading to
the ulx3s development board:
the ulx3s development board:
apio build --board ulx3s-45f
The output should look like this:
Ignore the warnings... Plug in the ulx3s into your computer using a microUSB cable - exciting times!
Next lets upload to the ulx3s development board:
apio upload --board ulx3s-45f
The output should look like this:
Finally its good practice to remove unnecessary files from the build process:
apio clean
As everything worked you should be able to see an LED flash like in the video below:
Again not the most exciting thing in the world but it is the hello world of FPGAS and from a tiny acorn an mighty oak tree grows ;)
That is all for now - Langster!
No comments :
Post a Comment