Beginner question on FIFO in "FPGA prototyping by VHDL examples"

I am very new to FPGA programming. I'm using "FPGA Prototyping by VHDL examples" to learn VHDL. I've gotten up to section 4.5.3 FIFO buffer, and am having trouble understanding what might happen in the code...

In the 'register file' section, array_reg(w_ptr_reg) is set to w_data.

In the fifo control section, w_ptr_reg is set to w_ptr_next.

Both events happen when clk'event and clk='1'.

Is there any guarantee as to which one happens first? How do I know whether array_reg(???) will use w_ptr_reg as the index, or w_ptr_next?

If there is anyone who is working through this same book, I'd like to start an email correspondence.

Bill

Reply to
Bill
Loading thread data ...

Well, I don't have the book, so I'm giving this advice blind - but - you're not thinking hardware. What hardware does the VHDL represent?

There's no "first" in the above - they're happening at the same time - at the postive edge of a clk.

Draw out what you think the hardware will look like. Sketch a few timing diagrams. Under what timing conditions will things work as intended?

Just use this thread, so others can contribute too.

Regards,

Mark

Reply to
Mark Curry

(snip)

Also, note that the FIFO is usually built from a dual-port RAM, so that it can write a new entry and read an old one at the same time.

Since you didn't mention it, I presume your FIFO doesn't have separate read and write clocks, though that is also allowed.

-- glen

Reply to
glen herrmannsfeldt

Don't confuse execution order with simulation time. Seqential statements execute in sequential order, one after the other, without consuming any simulation time (except a wait statement). Simulation time only advances while the process is suspended (e.g. waiting for the next clock edge, whether explicitly executing a wait statement, or implicitly at the bottom of a process that has a sensitvity list).

If w_ptr_reg is a signal, its value is not actually updated until (in execution order) the process suspends (to wait for another clock). So, references to a signal, whether they occur before or after (in execution order, not simulation time) the assignment to it, return the non-updated value. This is different from how software code works in typical programming languages.

If w_ptr_reg is a variable, it works just like software; the variable is updated immediately upon execution of the assignment statement (or procedure call, if it is an out or inout parameter). So references to a variable occurring before (in execution order) the assignment will return the non-updated value, but references to the variable occuring after (in execution order) the assignment will return the updated value.

Whether you use variables or signals, the synthesis tool will create hardware (i.e. gates and registers) that mimics the simulation behavior of the code. Whether that hardware is fits in the device or meets timing constraints is a different issue. I find it works best for me if I focus on coding the correct behavior, then worry about what hardware it generates if it does not fit or meet timing. Of course, with experience comes the knowledge of what types of behavior take too much resources or won't meet timing! Because of my preference and experience, I use more variables, saving signals for only inter- process communications.

Andy

Reply to
Andy

This is all true, but with a FIFO things get much more interesting.

Consider that the write data could come just before, at the same time as (more likely in simulation than actual practice) or just after the read cycle. The FIFO has to handle all three cases. (If the FIFO is empty, you shouldn't even try reading, and if full you shouldn't write.)

Some immediately pass data written to an empty FIFO to the output, others don't.

I usually write mostly structural verilog (module references and continuous assignment) but sometimes behavioral. When I do write behavioral verilog, I try to order the statements such that the result is the same as if they were executed sequentially.

(snip)

Well, real hardware timing may result in hardware that works and a simulation that doesn't, if you aren't careful with the way you write the statements.

-- glen

Reply to
glen herrmannsfeldt

I missed an important detail. The OP does not mention whether both assignment and reference are in the same process (I assumed they were). If the signal assignment is in one process, another process (clocked on the same edge of same clock signal) referencing the same signal will not see the updated value until the next clock edge. All VHDL processes scheduled to run in the same delta cycle must suspend before any of their driven signals are updated. If two or more processes are both sensitive to the same clock signal, then they will run in the same delta cycle. The issues of (non-shared) variables' update semantics affecting inter-process communication is completely avoided in VHDL, by not allowing (non-shared) variables to be accessed by multiple processes in the first place. Verilog's semantic parallel to VHDL's signal/variable is the non-blocking/blocking assignment. Unfortunately, verilog simulators are perfectly happy running code that performs blocking assignments and accesses in different "processes" (always blocks), even though the behavior is undefined/ erratic. This makes using these features riskier in Verilog (unless you have a good linting tool, which is a must with Verilog, IMHO).

Things to look for if simulation behavior differs from the hardware: Synthesis reported warnings/errors, Static timing analysis (STA) reported warnings/errors, Delta delay issues on clock signals (usually caused by assigning one clock signal from another, then moving data between processes clocked by both), Incorrectly defined multi-cycle or false path constraints (and not running full-timing simulations to verify them) Testbench issues (incorrectly representing actual HW interface counterparts), Synthesis or simulator bug, ... there are probably others.

In VHDL, with regard to variables, signals and orders of assignments and references, the behavior of synchronous processes (and therefore of the synthesized HW) is very well defined by the LRM, making differences in their behavior (excepting the above) extremely rare in my experience.

Andy

Reply to
Andy

[elided]

In real-world usage for Xilinx FPGAs, you do not write code that will be synthesized into a FIFo. You use their CoreGen tool to generate a FIFO block, and instantiate that into the controlling logic.

However, what others have already said about the essentially simultaneous nature of hardware versus the sequential programming paradigm holds true.

--------------------------------------- Posted through

formatting link

Reply to
RCIngham

Actually, in what passes for the real world here, I find a description that makes the Xilinx tools make an effective use of FPGA resources.

This has more to do with my personal strong anti-"wizard" bias, and a desire to write portable HDL. I suspect that if writing HDL took up a bigger proportion of my professional life than the 2% or so that it does, that my attitude might differ.

Yes. When you write software you should be thinking in the back of your mind what the processor will be doing (and possibly what the compiler's optimizer will do, first). When you write Verilog or VHDL you should be thinking in the back of your mind what logic will be generated. I have to admit that when I do this most of the blocks have "74HCxx" on them, but you still want to devote part of your brain to thinking what actual hardware will get used: it keeps you from doing really stupid things.

--
My liberal friends think I'm a conservative kook.
My conservative friends think I'm a liberal kook.
Why am I not happy that they have found common ground?

Tim Wescott, Communications, Control, Circuits & Software
http://www.wescottdesign.com
Reply to
Tim Wescott

r
e

I write vhdl a lot more than 2% of the time, and I share your anti- wizard approach for almost everything unless it is built-in HW that can only be accessed by primitives, like PLL/DLL, some two-clock FIFOs or that use two different data widths, etc. More often than not, I need some little special behavior for my specific application that precludes using the built-in stuff anyway, so I infer the RAM and roll my own control. Dealing with data in arrays is so much easier anyway.

I found out a long time ago that thinking about and then coding function and cycle-based behavior, rather than flops and gates, is more productive for me. When I need the utmost in performance (rarely), I normally use retiming, and it doesn't matter where/how I coded the registers anyway. If I need to speed it up, I normally approach it to see "what can I do ahead of time (in a previous clock cycle)?", or "what can I delay (into the next clock cycle)?", while I look for areas that I'm doing too much in one clock cycle. Unlike SW, where you generally don't want to do something until and unless you really need to, in HW it is almost the opposite. Do it just in case, then use the results when you need them. This generally results in less logic in the function, and therefore faster performance.

Andy

Reply to
Andy

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.