Intro to VHDL & FPGA? - Page 1
Forum section: electronics / beginners

author: mr_darker
author_rank: Contributor
timestamp: February 13, 2019, 06:20:50 pm
content: I've wanted to get into FPGAs for over 2 years now, had a MachXO3 development board the whole time, but it's never been a high enough priority. Til now.I am writing code to completely abstract the protocol vending machines use (MDB protocol). I've been writing it in C (I'm a programming noob btw), but suddenly I've come to the realization that it makes a lot of sense to have an FPGA act as a "bridge chip", so now I finally have a FPGA project.The simplest file which I will tackle first, is basically a stopwatch/timer, which when reset gets all values set back to zero (inactive/false/zero). When the time reaches a threshold value, a "flag" is set to indicate that condition. This timer is set at each falling edge (active) and set at each rising edge (inactive). I've never written a VHDL program before, so would like help either making sure it's good, or helping me accelerate towards that point. Also, I've been trying to stick to the Linux Kernel Coding style for my C code, which I'm sure I haven't done fully but I'm trying to do it naturally and make it habit. Is there a VHDL equivalent? I'd like to start out with good habits if I can. Also I'm using Lattice Diamond, but is there some sort of open source IDE or whatever Diamond is, for FPGAs? Much like EagleCad and Fusion360, I expect that I'll have wished I started out with the free/open-sourece software.A few side questions:I've sorta taken some example code for a shift register, and modified it (so that I can follow VHDL syntax) to be my code. A few things confuse me, the ':=' operator, which appears to assign/declare values, but why not '<=' instead? When I try to google it, I get more confused, reading that '<=' is a boolean less than or equal to operator for VHDL. Every tutorial and example code I've found, uses '<=' to assign a value, so maybe it's just where it's placed that matters?I believe I understand what processes are and the whole sensitivity list thing, but one question I have is about architectures. Can you have more than one defined for the same module? If so that'd be handy for the code later on that analyzes the data, as data is handled differently if you're a master or slave, but still would use the same "ports". Is there a quick/simple explanation on how to use two different architectures if you can have more than one? If not I'm sure I can look it up when the time comes.Is there a '#define' equivalent I can use in VHDL to put some human readable names on different values?Finally, I've followed a youtube tutorial for a development board similar to mine using Lattice Diamond, and when the spreadsheet came up where you assign pin values, there's also options to toggle fast or slow slew rate, why would you want slow? I'm aware there's timing stuff you gotta deal with (aware it exists, not aware of how to deal with it). There's also a hysteresis value I don't know anything about, and an 'outload' value (with picoFarads as the unit) which I also know nothing about. Are these "advanced" features I need not worry about yet, or are they essential functions for all designs?Oh I just remembered, I have one port as "inout", because it gets written to, then read from. But so does anything else? If I remember right, inout was for multiple sources that set it, so the 'time_elapsed' value could just be an IN type/direction right?VHDL CodeCode:library ieee ;use ieee.std_logic_1164.all;--------------------------------------------------------------------------------entity chronoLogic isPort (t_setup, t_break, t_response, t_inter_byte : out std_logic);Port (time_elapsed : inout std_logic)Port (reset : in std_logic);end chronoLogic;--------------------------------------------------------------------------------architecture check of chronoLogic issignal t_setup : bit := "0";signal t_break : bit := "0";signal t_response : bit := "0";signal t_inter_byte : bit := "0";signal microseconds : bit := "0";process(time_elapsed)beginif time_elapsed'event and time_elapsed = '200000' thent_setup <= '1';end if;if time_elapsed'event and time_elapsed = '100000' thent_break <= '1';end if;if time_elapsed'event and time_elapsed = '5000' thent_response <= '1';end if;if time_elapsed'event and time_elapsed = '1000' thent_interByte <= '1';end process;--------------------------------------------------------------------------------C CodeCode:--uint8_t chronoLogic(uint8_t command)--{-- if (command == TIMER_RESET) {-- microseconds = micros(); /*Each device has a local static copy of microseconds*/-- return OKKK;-- }-- else if (command == TIMER_UPDATE) {-- time_elapsed = micros() - microseconds;-- /* #HardwareAdjust Verify timing with a logic analyzer and adjust values if necessary*/-- if (time_elapsed > 200000) return T_SETUP; /*Bus Reset is over*/-- if (time_elapsed > 100000) return T_BREAK; /*Bus Reset Timer */-- if (time_elapsed > 5000) return T_RESPONSE;-- if (time_elapsed > 1000) return T_INTER_BYTE;-- }--}

author: mr_darker
author_rank: Contributor
timestamp: February 13, 2019, 06:24:20 pm
content: I do realize there's no counter to increment the 'time_elapsed'.That'd just be a Process, that is triggered by a clock, and has an adder to add 1 to the value of time_elapsed right?Also a reset function would be a process triggered by a reset input, which just sets everything to zero?

author: james_s
author_rank: Super Contributor
timestamp: February 13, 2019, 07:34:50 pm
content: It can be confusing, but the <= operator is used for assigning signals while the := operator is used for assigning variables. The water gets a bit muddy since effectively everything is a signal, HDL describes hardware and hardware has no inherent concept of variables but they are concepts that can be translated into hardware.

author: rstofer
author_rank: Super Contributor
timestamp: February 13, 2019, 07:38:58 pm
content: Quote from: mr_darker on February 13, 2019, 06:20:50 pmAs to := vs <=, see:https://stackoverflow.com/questions/11927144/what-s-the-difference-between-and-in-vhdlVariables will come up later on, or never, depending on how you write your code. I don't tend to use variables a lot for anything other than loop counters which are unrolled during synthesis anyway.

author: rstofer
author_rank: Super Contributor
timestamp: February 13, 2019, 07:47:24 pm
content: Quote from: mr_darker on February 13, 2019, 06:20:50 pmYou absolutely want to use the vendor tools because that is the only way you can instantiate proprietary cells like BlockRAM and the various port drivers/receivers. Yes, you can define an array instead of using BlockRAM but it's a waste of flip-flops. In a vendor tool, the array will most likely be converted to BlockRAM.Vendor tools are used every day in production environments - by professionals. They just work! There is a Verilog simulator for Linux (Google for it) and I'm not sure about VHDL. The thing is, simulation isn't hardware! I don't care how it simulates but I care very much if it works in hardware.The IDE is just a very small part of the provided tools. In fact, there is no requirement that you use it. Most toolchains can be run from the command line which means they can be run from a Makefile which means you can run them from just about any IDE you want. Worst case, you have a terminal window open for running 'make'.But why? I don't know much about the other tools but Xilinx Vivado is very nice (now that I have finally gotten over the shock of moving from ISE) and I can't see any reason to move to something else. To such an extent that I play with Xilinx chips exclusively.

author: rstofer
author_rank: Super Contributor
timestamp: February 13, 2019, 07:55:44 pm
content: Quote from: mr_darker on February 13, 2019, 06:20:50 pmOn my CPU project, whenever it failed to run, it was because I didn't provide a default value for some signal in a process. I was a slow learner! The problem comes up when you do stepwise refinement. I would add some more code and forget the default values for the new signals.

author: rstofer
author_rank: Super Contributor
timestamp: February 13, 2019, 08:25:34 pm
content: VHDL isn't C and thinking in terms of a linear program will get you in trouble. Within a few picoseconds, every syncronous circuit in the project can change state. Everything is parallel and it takes great effort to make it serial (state machine).Here is a bit of code that builds up the initial contents of BlockRAM from a file. I think this is done during place and route. The code is from hamster_nz and butched up by me.Note how generics are used, also note how files and input lines are used. In this case, I defined a 64k 16-bit memory as an array and then read in the initial contents from a file named LC3.hex.There's a lot of good stuff in hamster_nz's code. It's worth visiting his site - lots of projects, quality code. Search for his name here and then find a link to his site in his signature line.Code:library ieee;use ieee.std_logic_1164.all;use ieee.std_logic_unsigned.all;use STD.textio.all;use ieee.std_logic_textio.all;entity BlockRAM isgeneric (SIZE : integer := 65536;ADDR_WIDTH : integer := 16;COL_WIDTH : integer := 16;NB_COL : integer := 1;FILENAME : STRING := "LC3.hex");port (clk : in std_logic;we : in std_logic_vector(NB_COL-1 downto 0);addr : in std_logic_vector(ADDR_WIDTH-1 downto 0);di : in std_logic_vector(NB_COL*COL_WIDTH-1 downto 0);do : out std_logic_vector(NB_COL*COL_WIDTH-1 downto 0));end BlockRAM;architecture behavioral of BlockRAM istype ram_type is array (SIZE-1 downto 0) of std_logic_vector (NB_COL*COL_WIDTH-1 downto 0);impure function ocram_ReadMemFile(FileName : STRING) return ram_type isfile FileHandle : TEXT open READ_MODE is FileName;variable CurrentLine : LINE;variable TempWord : std_logic_vector(NB_COL*COL_WIDTH-1 downto 0); -- changed to use genericvariable Result : ram_type;beginfor i in 0 to 65535 loopexit when endfile(FileHandle);readLine(FileHandle, CurrentLine);hread(CurrentLine, TempWord);Result(i) := TempWord;end loop;return Result;end function;signal RAM : ram_type := ocram_ReadMemFile(FILENAME);beginprocess (clk)beginif rising_edge(clk) thendo <= RAM(conv_integer(addr));if we(0) = '1' thenRAM(conv_integer(addr)) <= di;end if;end if;end process;end behavioral;

author: AndyC_772
author_rank: Super Contributor
timestamp: February 13, 2019, 08:56:32 pm
content: Quote from: rstofer on February 13, 2019, 07:47:24 pmThe really compelling reason - which I don't think I've ever seen mentioned elsewhere - is that only the manufacturer of the device has an accurate timing model for the die.This timing model is absolutely essential to ensure that the setup and hold requirements inside the device fabric are met, and without it, there's no guarantee that the device will function reliably under any conditions. You can always create more setup time by slowing the clock down, but you can't improve hold margins that way, so without a guarantee that internal timing is met, there may be no frequency at which the design will operate.I know people have their reasons for wanting to go with open source software, but I remain to be convinced it's even possible to create an open source FPGA synthesis tool that produces a bit stream which is guaranteed to work. It's not like a compiler, where you can examine the object code and verify that it's correct; an FPGA configuration bit stream can be shown to be functionally correct, but that doesn't mean it can ever actually work.Accept that the FPGA vendor is provding a complete solution: the chip itself, and the tools which are required to configure it. Treat them as inseparable, and you'll be fine.

author: james_s
author_rank: Super Contributor
timestamp: February 13, 2019, 10:02:49 pm
content: I'm a big fan of open source, but you have to be a either a highly idealistic fanatic or a masochist to try doing FPGA development with an open source toolchain. The vendor supplied tools are free for personal use and they work quite well.

author: emece67
author_rank: Frequent Contributor
timestamp: February 13, 2019, 11:06:52 pm
content: .

author: emece67
author_rank: Frequent Contributor
timestamp: February 13, 2019, 11:48:50 pm
content: .

author: james_s
author_rank: Super Contributor
timestamp: February 14, 2019, 04:59:18 am
content: The most important thing to remember with HDL is that you're not writing a program. You're describing digital hardware with a special language that superficially resembles a programming language. Picture a schematic of digital logic, now to try to describe it in words, that is what VHDL and Verilog do. In many ways I think existing programming experience is a liability rather than an asset in this case. FPGA development is not programming, it is hardware design.

author: hamster_nz
author_rank: Super Contributor
timestamp: February 15, 2019, 08:26:44 am
content: I like talking VHDL. Feel free to PM me any questions that you are not brave enough to ask in public.I get the feeling that not many like my VHDL style tips, but here goes.- Avoid 'variables' like the plague. A ':=' is an abomination. You will know when there is no better way, and you are forced to use them.- In a process, try to avoid reading a signal after it is assigned. This will allow you to keep better track of things in your head. It will read like software code.- Consider using the "two process" methodology for a while.Seehttps://www.gaisler.com/doc/structdesign.pdf.It will be attractive for a while, then you seem to grow out of it once you 'grok' HDL more.- Only use STD_LOGIC and STD_LOGIC_VECTOR ports on the interfaces of modules.- Always use SIGNED and UNSIGNED in preference to INTEGER types.- When new to the game, use IP blocks or primitives for memory. Once they stress you out, then infer as many of these as possible.- If designing an simple FSM with lots of sequential states, ask yourself is a one-hot shift register might be a better idea (this will only make sense once you have a coded a few ugly FSMs)- If you have small counters (e.g. counting to 8 or 10) then a shift register might be a better counter.- Separate any I/O magic into their own module, and use the vendor primitives so you get exactly what you ask for.- Avoid clocking wizards. Use the vendor specific primitives so you get what you ask for.- Try to avoid vendor special features, where it makes sense. For example, I always use the vendor's FIFO when two clock domains are involved as it is just too hard to get it right.- Shift registers are cheap. Far cheaper than you would expect. Use them where possible.- Your job is to give the tools more than enough hints to give you the result you want. Don't think that the tools are super-smart. Give them no option but to give you what you want.- If somebody says "Ew! that code looks as if it was written by a 4-year old" but it works, then your job is done. Don't try to show how smart you are, try to show how simple the problem is.- Don't use Verilog. I'm converting a significant bit of VHDL to Verilog and I can not believe how much it hurts. Verilog takes any old tosh and makes it into a working design.

author: agehall
author_rank: Frequent Contributor
timestamp: February 15, 2019, 11:36:26 am
content: Quote from: hamster_nz on February 15, 2019, 08:26:44 amCurious - what version of Verilog? I've used some VHDL and I'm thinking of trying some SystemVerilog in my next project. From what I've seen, it looks pretty neat.

author: tggzzz
author_rank: Super Contributor
timestamp: February 15, 2019, 12:08:08 pm
content: Quote from: rstofer on February 13, 2019, 08:25:34 pmThere's no such link that I can see.Given hamster_nz's rules of thumb he outlined above, it may well be worth reading that.Overall my generic (i.e. language independent) "in the absence of more information" position is:as many FSMs as it takes to make the implementation easy to understand, design/test incrementally, and change later ontwo processes for each FSM: one for the asynchronous part, and one for the registersget the clock domain strategy sorted out before implementing anything, and use manufacturers blocks for every signal that crosses clock-domainsuse manufacturers blocks wherever possible, both primitive and "IP-level"; reinventing an ellipitical wheel makes people look stupidBut, of course, good taste is important and those rules of thumb can be brokenafter careful thought and understanding of the consequences.

author: rstofer
author_rank: Super Contributor
timestamp: February 15, 2019, 04:15:55 pm
content: Quote from: tggzzz on February 15, 2019, 12:08:08 pmTry:http://hamsterworks.co.nz/mediawiki/index.php/Main_PageThen try Google - there are quite a few replies to 'hamster_nz fpga'QuoteThen there is his book "Intro To Spartan FPGA" which focuses on the Spartan 3E. Kind of ancient at this point but it turns out I have a few Spartan 3 and Spartan 3E boards.https://github.com/hamsternz/IntroToSpartanFPGABook/blob/master/IntroToSpartanFPGABook.pdfI have not read the book but it's coming up soon!

author: rstofer
author_rank: Super Contributor
timestamp: February 15, 2019, 04:58:35 pm
content: Here's a hint for the newcomers, read the book I linked above. I'm just scanning through it but I'm impressed with the coverage. The book focuses on two boards, one of which is the Digilent Basys2 (which I happen to have) but the important part is that Digilent still has some of these boards for a Limited Time - they are obsolete.It is also true that the boards require the obsolete ISE WebPack toolchain. So? I still like ISE and since I have a bunch of Spartan 3/3E boards, I still have to have it installed. Once school is out you can transition to Vivado and the Artix boards. Or try to translate on the fly. One area of SUBSTANTIAL difference will be in the constraints file.Read the book! Even if you plan to start with Artix, it is worth the time to see how FPGA projects are created.

author: james_s
author_rank: Super Contributor
timestamp: February 15, 2019, 05:28:31 pm
content: Free Range VHDL is another book I found very helpful.http://freerangefactory.org/pdf/df344hdh4h8kjfh3500ft2/free_range_vhdl.pdfIt doesn't really matter what board you have in most cases, it's a little easier to use the same one used in a book but for the most part it's easy to port from one board to another.

author: rstofer
author_rank: Super Contributor
timestamp: February 15, 2019, 07:03:15 pm
content: It will be less easy to use a book written for the Spartan 3 and the ISE toolchain with an Artix board and Vivado. I have a tendency to try to buy the exact board an author is using. Since much of the book is dedicated to which buttons to push with ISE, using it with Vivado is going to be problematic.

author: james_s
author_rank: Super Contributor
timestamp: February 15, 2019, 08:32:14 pm
content: Well yeah, I would stick to something that can use ISE if the book uses ISE, there are lots of Spartan 3 and 6 boards out there though, I probably have half a dozen different ones. I tried Vivado and found it to be painfully bloated and ridiculously slow for the small projects I do, personally I like ISE better. I like Quartus much better than either one of those though, the only thing I've found that is better in ISE is the simulator, the rest is more clunky and requires twice as many clicks to accomplish the same task in many cases.

author: rstofer
author_rank: Super Contributor
timestamp: February 15, 2019, 09:02:05 pm
content: One advantage of using the same board and toolchain is that the constraints file given in the book might actually work and it delays needing to know how that file works. In my view, there is a quantum leap in complexity of the constraints file when moving to Vivado. I suppose with enough time and coffee it is understandable but, darn, the thing isn't anywhere near as intuitive as the ISE version.It doesn't matter what I think! Vivado is the new game in town, the Artix 7 chips are the new entry level devices and there is a boatload of logic in even the smallest variant. The 100T is a virtual dumpster for VHDL. Alas, there's never enough BlockRAM...