Where to get help with VHDL projects? - Page 1
Forum section: electronics / general

author: tec5c
author_rank: Frequent Contributor
timestamp: September 24, 2015, 06:16:08 am
content: Hi all,I am currently working on a simple 4-bit microcontroller design in VHDL. An instruction set of 16 instructions, 15 of which are being read from the memory, and 1 being written to the memory, i.e., storing the accumulator value, STA.I have implemented the 15 reading instructions (not 100% sure they're correct) and am trying to implement the STA instruction, however it seems to just break the whole design.What I mean is, when I run the simulation without STA being implemented, the transitionsseemcorrect. When I implement STA, everything is essentially stuck in reset mode. No transitions, all values 0 etc.I am at a loss as to how I can try to get help on this. Does anyone have any suggestions on how I can get some help? Do I post up the source code files as a txt file along with an image of the simulation diagram? Do I also post the instruction set? The state diagram?

author: hamster_nz
author_rank: Super Contributor
timestamp: September 24, 2015, 07:06:04 am
content: Hi,Can you tell me a little more about the memory interface? single port? 1R+1W port? Multiport? Inferred or ip core?Are you sure that the simulator isn't getting stuck due to a combinatorial loop? Or hitting an out-of-bounds index?The usual process in these cases is to try and isolate the problem as much as possibleif all else failed email it to me along with the test bench and a screenshot of the sim.

author: hamster_nz
author_rank: Super Contributor
timestamp: September 24, 2015, 09:31:51 am
content: tec5c and I had a chat... it boiled down to two things:Although external sram ships work like this...* set address, disable RAM's output drivers (OE),* output the data to write on the data bus,* raise the Write enable* wait a short while* drop the write enable... it don't work that way inside an FPGA.You need to have a clock and write the value to storage on the clock edge....Code:library IEEE;use IEEE.STD_LOGIC_1164.ALL;use IEEE.NUMERIC_STD.ALL;entity Memory_Two isport( clk : in std_logic;data_in : in STD_LOGIC_VECTOR(3 downto 0); -- Data Busdata_out: out STD_LOGIC_VECTOR(3 downto 0); -- Data Busaddr : in STD_LOGIC_VECTOR(7 downto 0); -- Address Buswr : in STD_LOGIC-- Read/Write);end Memory_Two;architecture table of Memory_Two istype a_mem is array( 0 to 255) of std_logic_vector(3 downto 0);signal mem : a_mem;begindata_out <= mem(to_integer(unsigned(addr)));process(clk)beginif rising_edge(clk) thenif wr = '1' thenmem(to_integer(unsigned(addr))) <= data_in;end if;end if;end process;end table;

author: Dago
author_rank: Frequent Contributor
timestamp: October 28, 2015, 08:02:39 am
content: Sorry for re-using this topic but I have a related question. Can anyone recommend me an example (preferably a big project, not simple stuff) of a _good_ VHDL project? I know there is a bunch of stuff in opencores etc. But I don't quite feel confident enough in assessing whether a project is well made or not. I'm especially interested in writing good test benches, feels like I might not be doing them as well as I could.Learning VHDL has been a challenge (thankfully I'm pretty persistent and like challenges...) mainly because finding material on it is very difficult. I have not even managed to find a proper syntax reference, just bits and pieces here and there.

author: Kalvin
author_rank: Super Contributor
timestamp: October 28, 2015, 09:13:15 am
content: Quote from: Dago on October 28, 2015, 08:02:39 amI found these books extremely helpful and practical when I needed something fast to get started with the FPGAs and VHDL:- Ott and Wildrotter, A Designer's Guide to VHDL synthesis- Mazor and Langstraat, A Guide to VHDL, 2nd editionThose books give practical examples on how to create something real that is usable, not just academic diibadaaba. Also,read the target device manufacturer's VHDL synthesis and testbench guidelines, and play with the examples.I didn't write the testbenches as I was in a hurry (no time to do things properly) and tested my design on the silicon oneblock at a time.Edit 1: I also found the book "Bricaud and Keating, Reuse Methodology Manual for System-on-a-Chip Designs" very valuablein order to be able to create maintainable design.Edit 2:Disclamer: This was 10 years ago, so there may be better and more comprenesive books available now. But those listedabove were the best I could find at the time which were focused on the practical, engineering side of the VHDL and synthesis.

author: hamster_nz
author_rank: Super Contributor
timestamp: October 28, 2015, 05:46:24 pm
content: Quote from: Dago on October 28, 2015, 08:02:39 amIn some ways, the VHDL or Verilog output is just like staring at assembler code. For the most part it tells you what is going on, but not WHY it is going on. For example, take an 8b/10b implementation. which is the core of many interfaces like Fibre Channel and DisplayPort. Implementation can be represented as a whole lot of random-looking logic (likehttp://opencores.org/websvn,filedetails?repname=8b10b_encdec&path=%2F8b10b_encdec%2Ftrunk%2F8b10_enc.vhd) or a couple of lookup tables like I did in (https://github.com/hamsternz/FPGA_DisplayPort/blob/master/src/data_to_8b10b.vhd).Both tell you exactly what is going on, but neither will tell you why it is done that way.And that also extends to the comments in code - comments are usually directed at telling the reader at how the code achieves it's function, not what that function is.

author: Dongulus
author_rank: Regular Contributor
timestamp: October 28, 2015, 06:37:41 pm
content: I agree with the hamster_nz in regards to how the 'why' of the logic in an HDL is not apparent and takes some digging to figure out. I felt similar to Dago in wanting to figure out what well written logic looks like when I started working out of college and being put onto projects where I had to modify and generate VHDL. Over that time, I have spent so much time trying to figure out how so many different VHDL modules work that I have a better understanding about what I, as someone approaching a logic module without any understanding of its function, would like to see.I suggest you just start reading through as many source files on the internet that you can find and build a set of skills for deciphering HDL logic. This is the only way I know to really figure out which practices help or hider someone to understand the underlying logic.

author: jancumps
author_rank: Unknown
timestamp: October 28, 2015, 09:02:34 pm
content: I'm learning with @hamster_nz's own tutorial:http://hamsterworks.co.nz/mediawiki/index.php/FPGA_course

author: hamster_nz
author_rank: Super Contributor
timestamp: October 28, 2015, 09:09:52 pm
content: Quote from: jancumps on October 28, 2015, 09:02:34 pmI really should get around to refreshing all of that - it doesn't use SIGNED or UNSIGNED types, and it is just the minimal set of VHDL that allows you to do anything. It is in need of an overhaul

author: jancumps
author_rank: Unknown
timestamp: October 28, 2015, 09:17:23 pm
content: Whenever I build one of your exercises, I convert to those types (and also when trying out other people's examples, I make changes related to taboo libraries).So I learn from your non-compliance

author: AndyC_772
author_rank: Super Contributor
timestamp: October 28, 2015, 09:26:56 pm
content: IMHO, the biggest problem with learning to program and use FPGAs is simply unfamiliarity. There are several hurdles to try and overcome all at the same time, some of which are obvious, others much less so.The language is new, and entirely unlike a sequentially executed language like C, or BASIC, or assembler.The device manufacturer's tool chain is bespoke, though it's probably more familiar, and easier to get to grips with once you've found the way to assign a device, its pinout, and the set of files that make up the source code for the project.Then there's the nature of the device itself. It's very, very much NOT a microprocessor, and you certainly can't think of it in the same terms even though it may end up doing some of the things that could otherwise be done by one.Most of the FPGA projects I've done have tended to contain:- PLLs- a chunk of logic which communicates with a host processor (maybe SPI, or I2C, or something very different and bespoke);- a state machine which performs whatever sequence of events the FPGA is needed for;- all the (possibly unrelated) glue logic that the board needs, because once you've got one, it's daft not to soak up this stuff into the FPGA too.Is using an FPGA "hard"? I'd have to vote 'yes'.There are a lot of 'gotchas', and if you've not had to consider the timing of signals in your design before, you may be in for a nasty surprise. You'll also get to know and "love" the effect of metastability, especially if you have more than one independent clock in your system.For example, suppose you have a chunk of logic which is clocked from a crystal on the board, and its behaviour has to be configurable in some way. Normally it'll be configured by implementing registers that can be written by a host CPU.For as long as that register's value remains unchanged, your code may work perfectly well. But what if it gets updated at exactly the same time as an active clock edge arrives? There's no definitive answer BTW other than "it depends...", but there's a non-zero chance your design will misbehave, or stop working entirely. The fault might manifest itself immediately, or after a few seconds, or after it's been in service for years.Reliable signalling between clock domains is a major, major part of any non-trivial FPGA design. In a microcontroller it's all done for you by the manufacturer of the device; if a peripheral is slower than the core, or asynchronous to it, the logic is already done for you to cope with the mismatch. In an FPGA you have to do it.

author: Kalvin
author_rank: Super Contributor
timestamp: October 29, 2015, 08:39:43 am
content: Here are two nice articles about the problem of multiple clock domains and the solutions:http://opencores.org/articles,1199340900http://www.edn.com/electronics-blogs/day-in-the-life-of-a-chip-designer/4435339/Synchronizer-techniques-for-multi-clock-domain-SoCsThe FPGA manufacturer will have specific application notes on how to take care the metastability in their specific devices and their tools.

author: Dago
author_rank: Frequent Contributor
timestamp: October 29, 2015, 09:00:59 am
content: Personally I *think* I'm pretty well in grips with basics of HDL-design and clock domains and the whole concept in a sense. I even feel like it is easier to follow/write HDL (to a point, doing anything more complex is of course rather tedious) than for example microcontroller stuff because you have implemented everything yourself and there is no black box like with microcontrollers. However I feel like I'm working with a very limited toolbox and I can't even find proper examples showing anything more complicated (like something with procedures/subprograms/generate etc.), I haven't even found a good list/reference of what the tools available even areI'll show an example of the type of crap I've had to deal with recently when learning, this took me and a friend a good couple hours to solve:Code:process(clk, reset)type state is (idle, second, third);procedure handle_delay( constant delay : in natural;variable timer : inout natural;constant next_state : in state;variable state_var : out state) isbegintimer := timer + 1;if timer = delay thenstate_var := next_state;timer := 0;end if;end procedure;variable cur_state : state;variable timer : natural;beginif reset = '1' thencur_state := idle;timer := 0;elsif rising_edge(clk) thenif cur_state = idle thenif input_signal = '0' thenhandle_delay(666, timer, second, cur_state);elsetimer := 0;end if;elsif cur_state = second thenhandle_delay(555, timer, third, cur_state);report "State: second";elsif cur_state = third thenhandle_delay(444, timer, third, cur_state);report "State: third";end if;end if;end process;If input_signal = '0' for 666 cycles or more, can anyone explain why it will enter second state exactly once (single "State: second" printout) and no more...

author: hamster_nz
author_rank: Super Contributor
timestamp: October 29, 2015, 10:37:44 am
content: Quote from: Dago on October 29, 2015, 09:00:59 amI have no idea what is going on there! However I've got a few simple rules that keep me out of trouble.a) Try really hard to avoid using variables - use signals instead.b) If you must use a variable, always assign them at the start of a process to ensure that they have no 'life' across clock cycles. This ensures that they only hold working values.c) Where possible, order all of the assignments in a process such that you get the same result if you didn't know about delayed assignments. This boils down to 'don't use a signal after you assign a new value to it'. It gives you one less thing to think about when debugging - your signals don't have a "current value" and "next value" for you to keep track of. One exception might be a default value which is assigned at the start of a process.d) If you really must use a variable, include something in the name to hint that it is a variablee) And always know the size and range of your signals - STD_LOGIC, STD_LOGIC_VECTOR, SIGNED and UNSIGNED only for me! Once you do this for a while, it isn't a big issue as 1000 is about 10 bits, so to hold 1,000,000 takes 20 bits.So I try not to write like this:Code:timer <= timer+1;if timer = 999 thentimer <= (others => '0');end if;But I write it like this:Code:if timer = 999 thentimer <= (others => '0');elsetimer <= timer+1;end if;It is a bit more wordy, but clear that timer will run from 0 to 999, and not 0 to 998...Also, if you don't know the length of data you are using, then use unbounded vectors on the external interface and use the range and high attributes to size the internal signals appropriately.Code:entity pulse_per_count isport (clk : in std_logic;terminal_count : in std_logic_vector;pulse : out std_logic;)end entityarchitecture arch of pulse_per_count issignal count : unsigned(terminal_count'high downto 0) := (others => '0');begincount_proc: process(clk)beginif rising_edge(clk) thenif count = unsigned(terminal_count) thencount <= (others => '0');pulse <= '1';elsepulse <= '0';count <= count + 1;end if;end if;end process;end architecture;You can then use it for any size of terminal_count you want... 1 bit or 100 bits.That's just how I do it - I'm sure others will do it differently.

author: Kalvin
author_rank: Super Contributor
timestamp: October 29, 2015, 11:43:03 am
content: hamster_nz has words of wisdom

author: tggzzz
author_rank: Super Contributor
timestamp: October 29, 2015, 12:05:23 pm
content: Quote from: AndyC_772 on October 28, 2015, 09:26:56 pmVery true.Manufacturers often have predefined blocks for data and control signals that cross clock domains. Understand their limitations, use them, and don't reinvent the wheel - because it will probably turn out to be eccentric!The other major topic is to think in terms of FSMs and use a consistent design pattern that clearly expresses the FSM's intended design.

author: AndyC_772
author_rank: Super Contributor
timestamp: October 29, 2015, 12:24:54 pm
content: For those reading along who are unfamiliar with VHDL...One really important thing to understand about VHDL, is that it's describing a piece of hardware. It's NOT a sequence of instructions to be executed one after another.When a signal is assigned a value, the assignment doesn't take place "immediately", as in, any references to the assigned signal from that point onwards will refer to the new value. "Later" assignments - and by 'later' I mean 'further down in the source code' rather than 'after more time has elapsed' - take precedence over "earlier" ones.Here's a few examples...Swapping the contents of two registers each time a clock pulse occurs:Code:IF clk'event AND clk = '1' THENa <= b;b <= a;END IF;Although this looks like the classic example of how NOT to swap two values in code running on a microprocessor, it does indeed swap the registers in an FPGA. This is because the value of each register is effectively frozen at the instant of the clock edge. The hardware described by the code performs its logical function at that instant in time, and the new register values it assigns take effectafterthe clock edge. Note that transposing the two lines of code inside the IF statement would result in exactly the same hardware. Think of the '<=' operator as meaning "willtake its new value from...", rather than "immediately takes its new value from".Another useful trick is to produce an output which is only active under specific conditions, and is guaranteed to be inactive otherwise:Code:IF clk'event AND clk = '1' THENdata_ready <= '0';IF (set of very specific conditions) THENdata_ready <= '1';END IF;END IF;I use this method all the time to control writes into FIFOs. Usually I only want a single write to occur, at the first occasion when the value to be written is available. Taking advantage of the 'later assignments override earlier ones' feature means I only have to refer to the 'data_ready' signal twice in the code: once when I want it to be active, and once which covers all other possible conditions.Variables differ from signals, in that they actually do take their new values 'immediately'. So:Code:SIGNAL s : INTEGER;VARIABLE v : INTEGER;IF clk'event AND clk = '1' THENv := 1;v := v + 2;s <= v;END IF;...results in the register 's' taking the value 3. But:Code:SIGNAL s : INTEGER;SIGNAL v : INTEGER;IF clk'event AND clk = '1' THENv <= 1;v <= v + 2;s <= v;END IF;... would result in 's' taking the value of 'v', whatever that happened to be at the end of the last clock cycle, and the value of 'v' increments by two. The assignment v <= 1 is completely lost, because it's overruled by the following line.

author: Dago
author_rank: Frequent Contributor
timestamp: October 29, 2015, 01:24:23 pm
content: Thanks for the comments!However I cannot think of a single reason to prefer signals to variables. Can you elaborate?For me variables have been working just as expected. If you'd use signals instead it would tend to make the whole thing in to a mess, for example copying a process to some other component would become tedious. Also the distinction is very clear that variables are local to the process and signals are for communicating between processes.And the answer to my "puzzle" is that that procedure seems to deposit a default value for state_var (which seems to be the first value of the state enum; idle) unless you explicitly assign something to it. Just changing the state_var to be "inout" instead of "out" solves the problem.

author: AndyC_772
author_rank: Super Contributor
timestamp: October 29, 2015, 01:42:55 pm
content: A 'signal' is physically implemented as a memory element, like a D-type. It's guaranteed to maintain its value from one cycle to another.A 'variable' is a logical convenience; a way to describe functionality which might otherwise become unwieldy.Variables are temporary, though. They don't necessarily persist from one invocation of a process until the next - although the synthesis tool might store their values for you if that's what your code implies. (No doubt someone will be along shortly to explain what's actually guaranteed here; I've always written code on the assumption that variables are lost at the end of a process).Here's an example of how variables and signals are used. Imagine you want to shift a byte left by four places, one way to do it might be:Code:SIGNAL s : STD_LOGIC_VECTOR (7 DOWNTO 0);VARIABLE t : STD_LOGIC_VECTOR (7 DOWNTO 0);IF clk'event AND clk = '1' THENt := s;FOR i IN 1 TO 4 LOOPt (7 DOWNTO 1) := t (6 DOWNTO 0);t (0) := '0';END LOOP;s <= t;END IF;You'd never actually write this particular code, of course, because you can do the shift in one go. It does, however, illustrate how the 'immediate update' feature of a variable can be useful.Because the intent of the code is evaluated at compile time, it should synthesize to exactly the same as if I'd written:Code:SIGNAL s : STD_LOGIC_VECTOR (7 DOWNTO 0);IF clk'event AND clk = '1' THENs (7 DOWNTO 4) <= s (3 DOWNTO 0);s (3 DOWNTO 0) <= (OTHERS => '0');END IF;It's important to understand that there's no penalty for using the loop, because all I've done is describe the same hardware in two different ways. No actual time elapses as the loop 'executes'; all that happens is the synthesis tool has to work out where all the bits end up at the end of the process.

author: Dago
author_rank: Frequent Contributor
timestamp: October 29, 2015, 04:38:09 pm
content: Why would the variables not persist or why assume they wouldn't? I haven't checked what the standard says but I'm pretty sure they're guaranteed to retain their value (also they have worked as expected in the simulator and in the hardware)... Also "invocation of a process" sounds weird to me, as far as I've understood a process just "is" and its state just gets re-evaluated when a signal in the sensitivity list changes state?

author: hamster_nz
author_rank: Super Contributor
timestamp: October 29, 2015, 05:49:44 pm
content: Quote from: Dago on October 29, 2015, 01:24:23 pmFor me, signals match my mental model of the synthesized logic, where as variables don't. Say you had to output the the first twelve words out of blocks of sixteen, over a memory that is 256 words in size.I guess this is completely valid:Code:....-- where addr is a signal of 'integer' typedata_out <= memory(addr);-- increment the counteraddr_var := addr+1;-- skip the four we don't want to seeif addr_var = 12 thenaddr_var := addr_var + 4end if;-- reset the counter to keep it in bounds of the memory arrayif addr_var = 256 thenaddr_var := 0end if;addr <= addr_var;....However, I can't see what the logic for that would look like in my head - how many levels deep will it be (will it be slow or fast? will it consume lots of logic? If i code like that I leave that up to the tools and hope it will be right.I would code it as follows:Code:....-- where addr is a correctly sized unsigned signaldata_out <= memory(to_integer(addr));-- skip over the 12,13,14 and 15 entries in every block of 16if addr(3 downto 0) = 11 thenaddr(3 downto 0) <= (others => '0');addr(addr'high downto 4) <= addr(addr'high downto 4)+1;elseaddr(3 downto 0) <= addr(3 downto 0)+1;end if;....This will produce the faster running design, with the following structure (or something very close):- A LUT4 to look for 11 in the lower four bits- Four adder for the low half, adding the constant '1'- A four bit adder, for the high half.- The lower four bits of the addr register have reset hooked up to the LUT4 output- The upper four bits of the addr register have their CE wired to the LUT4 output.The earlier code that uses variables prevents me from seeing the final implementation, I can't guesstimate the resource utilization, I can't guesstimate the timing.I guess a lot of people will argue that I should use the tools to hide these trivial things from me, and code at a higher level of abstraction. They have a point, but I don't like re-writing code that has after it has already been simulated and verified because it is too slow due to hidden complexity. For me, calling out to external procedures that don't resolve at build time and using variables allow logic in code destined for synthesis is asking for problems at the tail of the design process, where it is costly to fix.Oh, and the inconsistent usage of variables and signals just does my head in. So just decide and use one or the other as being your style, and then have flame wars with those stupid idiots who are on the other side. It's a tradition!

author: jahonen
author_rank: Super Contributor
timestamp: October 29, 2015, 08:32:34 pm
content: I have always thought that if value of the variable is read after it is modified in a clocked process, then it is a combinatorial path (using registered value as a starting point) and possibly an performance bottleneck (but acceptable if used properly). But if a variable is read/used before it is modified then it is an output from a register and is identical to the signal behaviour. Using an variable gives easy choice whether use the clocked or combinatorial version (possibly saving some latency and design time giving sometimes much easier to read/less error-prone/easy to maintain code) IF timing constraints permit. All credits to those who can code something like a complex video/image compression algorithm using HDL just by thinking LUTs, ones and zeros but rest of us need some help from a computerSo I think that speed comes from simplifying combinatorial logic into small digestible pieces, which is what the above examples demonstrates. I also notice that they are not completely equivalent, first one skips only values 12-15 whereas second one skips as described.Regards,Janne

author: AndyC_772
author_rank: Super Contributor
timestamp: October 30, 2015, 11:08:32 pm
content: Quote from: Dago on October 29, 2015, 04:38:09 pmIn that case I think it's I who have learned something, not you!With hindsight, there's a clue in the structure of the language. Signals are declared outside of all processes, and variables are declared within them. This would be consistent with the idea of a variable as being the more appropriate type to use within the scope of a process, while a signal is used for information which must be communicated from one process to another.QuoteYes, of course, it's just a chunk of hardware responding to changes in its inputs - but in software terms you might like to think of it as analogous to an interrupt service routine, which gets invoked every time an external (to it) signal changes state.