I am starting at VHDL and while trying to implement a circuit I found that I need basic elements like leading one/zero detectors or leading one/zero counters. Since I consider these basic I would like to know if there is a library to include.
If I have to put together my own implementations is there a collection where one can copy from or do I need to reinvent the wheel?
(I can google them one by one and find implementations in forums, however some of them seem quite bad/implemented not general enough (i.e. this multiplexer)).
One reason why the approach you suggest (a library of simple re-usable elements) doesn't catch on : you are often better off without them.
The example multiplexer you linked to illustrates the point quite well:
ENTITY multiplexer IS
PORT (
a, b, c, d, e, f, g, h : IN STD_LOGIC;
sel : IN STD_LOGIC_VECTOR(2 DOWNTO 0);
y : OUT STD_LOGIC
);
END ENTITY multiplexer;
given slightly better design, using the type system you could write (perhaps in a re-usable package)
subtype sel_type is natural range 0 to 7;
and then in your design, given the signal declarations
signal sel_inputs : std_logic_vector(sel_type);
signal sel : sel_type;
signal y : std_logic;
you could simply write
sel_inputs <= a & b & c & d & e & f & g & h;
y <= sel_inputs(sel);
with much less fuss than actually instantiating the multiplexer component.
Leading zero detectors look harder at first glance : they are generally implemented as clocked processes, or as part of a larger clocked process.
Recent trends in VHDL are moving towards a semi-behavioural style of synthesisable VHDL, where quite a lot is written in a sequential style within a single clocked process. This is best seen in the "single process state machine"; smaller, easier to understand and definitely more reliable * than the "two process" style still commonly taught.
(* unless your tools support the "process(all)" sensitivity list of VHDL-2008 for the combinational process.)
Within a clocked process (i.e. assume the following is inside if rising_edge(clk) then simple tasks like counting the leading zeroes in an array can be expressed using for loops; for example:
for i in sel_type loop
if sel_inputs(i) = '1' then
zero_count <= sel_type'high - i;
end if;
end loop;
The last assignment will win; if h = '1' the count will be 0, otherwise if g = '1' the count will be 1, and so on.
Now it's not a one-liner unless you write a procedure for it (also synthesisable with most tools!) but compare it with the size of an entity instantiation (and interconnections).
Of course a library of procedures and functions can be useful; and (for less trivial tasks) the same is true for entities also, but in my opinion, making them general enough to be useful to all, without making them heavyweight, would be quite an achievement.
some IDEs come directly with an example library. e.g. Xilinx ISE has the "language Templates" section with basic synthesis constructs, coding examples,...
Related
Is there some way to define an alias, function or subtype in a package to define syntactic sugar around constrained vector declaration?
I often declare port and signals in VHDL as std_logic_vector(N - 1 downto 0). I would like some syntactic sugar around this. something similar to this:
context work.common;
entity X is
generic(
byteSize : integer := 8
);
port(
DataIn : in logic_bus(byteSize);
DataOut : out logic_bus(byteSize)
);
end entity;
architecture X_arch of X is
signal DataSignal : logic_bus(byteSize);
begin
DataOut <= DataSignal;
DataSignal <= DataIn;
end architecture;
I would like to have this syntactic sugar defined all over my project, i.e., avoid defining a subtype in the architecture if possible. I'm very confused with the usage of open, natural range <>, the difference between type and subtype, and what kind of things functions can return.
regarding constraints inference
I would like to stress that I'm looking for a way to abstract away the definition of similar vector constraints.
The proposal to leave my vector completely unconstrained, while a clever workaround, has strings attached to it that I would like to avoid :
It is not compatible with type casting see here.
It doesn't communicate the relationship between ports (for instance it doesn't communicate that DataIn and DataOut are supposed to be the same size)
It risks to allow badly sized bus designs to pass synthesis by mistake (accidental meaning)
For this reasons, my workplace coding style has banned unconstrained types in ports in favor of generics. Thank you for your understanding.
I assume that you want logic_bus(x) to be some kind function/alias/subtype to abstract away std_logic_vector(N-1 downto 0), working around constrained vector declaration.
VHDL have a quite small user base in terms of numbers. So much of what of the language you can use is highly tool dependent. Make sure you have read up on the supported features of vhdl-2008 for your development environment. As a main user of Xilinx UG901 is my go to for Vivado. Even though a feature is supported you might sometime be required to add an keep attribute to make it not be optimized away in synthesis.
Since a few years back Vivado have a quite full support for vhdl-2008
Unbounded arrays have been supported for even longer.
Generic package have just seen the light of day and seems to be a bit different in Mentor and Xilinx interpretation, but you can make it implement.
Generic types is a part of the language, but I have never got it to work the way I needed it to. (I always try to keep to sl and slv in entity)
As I'm not sure about your environment I can only answer for mine.
Full VHDL-2008 support is assumed
Unconstrained slv
Working around constrained vector definition by avoiding it.
entity X is
port(
DataIn : in std_logic_vector;
DataOut : out std_logic_vector
);
end entity;
architecture X_arch of X is
-- guiding DataSignal with to have a relationship with some signal is usally needed
signal DataSignal : std_logic_vector(DataIn'range);
begin
assert DataIn'length = DataOut'length
report "length missmatch of input and output data"
severity ERROR;
DataOut <= DataSignal;
DataSignal <= DataIn;
end architecture;
Asserts will need to be enabled for synthesis, ether for the run or appended to the synthesis strategy. (see ug901, for Xilinx or check vendor docs)
Unconstrained vectors need to be constrained at the top level. Some times a bit flakey in multi layered component designs. Signals are usually a bit iffy about how they get inferred.
Higher dimensional data can also be unconstrained
type slv_2d is array(natural range <>) of std_logic_vector;
-- Constrain multiple dimensions at once
-- (this might still be broken in GHDL, open issue last time i checked)
-- synthesis/implementation in Vivado works
-- simulates in Modelsim/Questa works
signal test_signal : slv_2d(0 to 5)(7 downto 0);
-- constants can de inferred from assignment
constant test_signal : slv_2d := ("001", "010", "011", "100" );
subtype I usually think about as limiting a type further.
-- integer may be any value within -2147483648 to +2147483647.
-- I might have an issue or a design restriction to highlight
-- I usually don't use subtypes
subtype my_range is integer range 0 to 255;
-- Will throw error if outside of range 0 to 255.
variable cnt : my_range := 0;
I really recommend reading ug901 "Supported VHDL-2008 Features" section of the latest release, even if you don't work with Xilinx. Dolus have a few really good writeups on vhdl-2008 that might strike your fancy and will talk about related features to what you are looking for.
I have a VHDL BCD counter whose output is an integer value (digit).
But when I simulate the code in Xilinx ISE it shows the code's waveform in binary value. The code works but the output should be integer but it's not. I have tested this code in Modelsim and the output is correct and it's in integer value. This problem is in code synthesize too and the value is binary.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity bcdcnt is
Port ( clk : in STD_LOGIC;
digit : out INTEGER RANGE 0 TO 9);
end bcdcnt;
architecture Behavioral of bcdcnt is
begin
count: PROCESS(clk)
VARIABLE temp : INTEGER RANGE 0 TO 10;
BEGIN
IF (clk'EVENT AND clk = '1') THEN
temp := temp + 1;
IF (temp = 10) THEN temp := 0;
END IF;
END IF;
digit <= temp;
END PROCESS count;
end Behavioral;
That's what synthesis does.
That's what synthesis MUST do : it translates your high level design to the resources in your FPGA or ASIC, which are binary. So, what's the problem here?
If you need to simulate the post-synth result, the usual approach is to create a wrapper entity that takes the correct port types, and translates between those and the post-synthesis netlist component.
Then, simulation should work with either the original entity, or this wrapper entity, both of which have integer ports.
Better still, you can re-use the same entity, and add the wrapper as a second architecture, thus guaranteeing that it uses the same interface (ports).
(You can even instantiate both the original and post-synth in its wrapper in the testbench, in parallel with a comparator on their outputs, to see they both do the same thing. But note there will be gate-level delays between them; usually you check outputs only on clock edges so these do not matter.)
Another approach is to restrict port types on the top level of the design to binary types like std_logic_vector. This plays nicer with badly designed tools, like ISE where the automatically generated testbench will have binary port types, (I generally edit them back to the correct ones; it's almost easier to write TBs from scratch).
But it restricts you to using an obscure and complex design style instead of higher level abstractions like Integers.
It's bad - really bad - that this approach is taught and encouraged so widely. But it is, and sometimes you'll just have to live with it. (Even in this approach, there's no reason to avoid decent abstractions internal to the FPGA, as long as the synthesis tool understands them).
A third approach - roughly, "trust, but verify" - is to trust that synthesis tools are competently written - which is usually true - and forget about post-synthesis simulation.
Just verify the design thoroughly at the behavioural level in simulation, then synthesise it, and test in live FPGA.
99% of the time (unless you're writing really weird VHDL), synth and P&R have done the right thing, and any differences you see are due to the aforementioned I/O timings (gate delays at the I/O pins). Then model these in the testbench and/or wrapper until you see the same behaviour in both (fix anything that needs fixing, and re-synthesise).
In this approach you only need to bother with the (MUCH slower) post-synth and post-PAR simulations if you need to track down a suspected synthesis tool bug.
This does happen : I've seen two in a quarter century.
Most of the time I just use the third approach.
I'm practicing VHDL, and I have a fundamental question about "simple" statements which do not require a process.
I would like to know the difference between
c <= a and b;
Where the statement is not inside a process, just written after the architecture begin, and
process(a,b)
begin
c <= a and b;
end process;
Will these results produce the same thing?
Ty :)
Yes, the two descriptions are equivalent.
The concurrent signal assignment c <= a and b is evaluated at each update of any of the argument (a or b), and the process will also evaluate each time any of the arguments in the sensitivity list is updated (a or b).
In the simple example it not required to use a process, but for more complex expressions, the process has the advantage that control structures like if and for can be used, which is not directly possible in a concurrent signal assignment. Also, for sequential logic a process is required.
You can think of any VHDL one liner as an implied process with the arguments on the RHS of <= in the sensitivity list.
This is why both of the code snippets you wrote are practically equivalent.
I am trying to make a BCD converter to show numbers from 0 to 9999, I need to implement Double Dabble Algorithm using the shift operators. But I just cannot start coding without running into warnings i dont really know about, I am still a beginner so please ignore any stupid mistakes that I make. I started off by first implementing the algorithm. I have never used shift operators so I am probably not doing it right, please help, here is my code
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity algorithm is
Port (x: in unsigned (15 downto 0);
y: out unsigned (15 downto 0));
end algorithm;
architecture Behavioral of algorithm is
begin
y <= x sll 16;
end Behavioral;
And the error
Xst:647 - Input <x> is never used. This port will be preserved and left unconnected
if it belongs to a top-level block or it belongs to a sub-block and the hierarchy of
this sub-block is preserved.
Even if I implement this
y <= x sll 1;
I get this error
Xst:647 - Input <x<15>> is never used. This port will be preserved and left
unconnected if it belongs to a top-level block or it belongs to a sub-block
and the hierarchy of this sub-block is preserved.
What am I doing wrong here?
What you are doing wrong is, firstly, attempting to debug a design via synthesis.
Write a simple testbench which, first, exercises your design (i.e. given the code above, feeds some data into the X input port).
Later you can extend the testbench to read the Y output port and compare the output with what you would expect for each input, but you're not ready for that yet.
Simulate the testbench and add the entity's internal signals to the Wave window : does the entity do what you expect? If so, proceed to synthesis. Otherwise, find and fix the problem.
The specific lines of code above, y <= x sll 16; and y <= x sll 1; work correctly and the synthesis warnings (NOT errors) are as expected. Shifting a 16 bit number by 16 bits and fitting the result into a 16 bit value, there is nothing left, so (as the warning tells you) port X is entirely unused. Shifting by 1 bit, the MSB falls off the top of the result, again exactly as the warning says.
It is the nature of synthesis to warn you of hundreds of such things (often most of them come from the vendor's own IP, strangely enough!) : if you have verified the design in simulation you can glance at the warnings and ignore most of them. Sometimes things really do go wrong, then one or two of the warnings MAY be useful. But they are not a primary debugging technique; most of them are natural and expected, as above.
As David says, you probably do want a loop inside a clocked process : FOR loops are synthesisable. I have recently read a statement that WHILE loops are often also synthesisable, but I have found this to be less reliable.
This is a question for those who have a good understanding of VHDL. I'm a newbie but so far I have been generating VHDL using a behavioral description. For me it is much easier to think about as it is similar to writing software. I am aware that a possible downfall is that behavioral 'executes' sequentially while structural executes concurrently within the design component/process..
So I'm just curious, if I have an architecture that uses a process for say an 8-bit shift register (SISO) and I want to create 4 instances of these (4x8-bit shift registers) would I create a component and 4 instances of the process?
Or would I generate 4 processes (executing in parallel to one another) and just call each process by a different name?
Also, just a general question to get a consensus of what good practices people use out there, which do you prefer: structural vs. behavioral?? When would be a good time to choose one over the other? I'm guessing their could be some benefits with 'faster' execution using components that allow internal concurrency vs. sequential execution in processes.. It sure does seem to me though that one could reduce the design time with behavioral designs..
Thanks!
~doddy
For my money, the role of structural HDL nowadays is restricted to interconnecting tested working behavioral blocks (or connecting untested ones to their testbenches!) - I would agree with you about the superiority of behavioural VHDL in terms of design creation and test time.
I would also agree that writing a behavioural process is in some ways similar to writing software (over the screams of some that it isn't)
HOWEVER
do not fall into the trap of equating behavioural with sequential or slow!
Given your shift register, say
type reg_type is array(7 downto 0) of bit;
signal s_in, s_out : bit;
process(clk) is
variable reg : reg_type;
begin
if rising_edge(clk) then
s_out <= reg(7);
reg := reg(6 downto 0) & s_in;
end if;
end;
I can trivially parallelise it as follows:
signal p_in, p_out : array(1 to 4) of bit;
process(clk) is
variable reg : array(1 to 4) of reg_type;
begin
if rising_edge(clk) then
for i in reg'range loop
p_out(i) <= reg(i)(7);
reg(i) := reg(i)(6 downto 0) & p_in(i);
end loop;
end if;
end;
(And yes there are simpler ways to write this!)
It is important to note that the loop does not take any longer to run : it just generates more hardware (in software terms, it is unrolled completely). Each iteration is completely independent of the others; if they weren't, things would be more complex.
Don't worry about the academic differences between structural and behavioral.
Worry about the difference between signal assignment scheduling and variable assignment scheduling in a process (learn what delta cycles and postponed assignment are - this is one of the key diffs from software, and arises because software only has variables, not VHDL's signals). That will explain why I implemented the pipeline upside down here (output first) - because I used a variable.
Worry about why so many people teach the idiotic 2-process state machine when the 1-process SM is simpler and safer.
Find and understand Mike Treseler's pages about single-process models (I hope they are still online)