Process or not to Process? - vhdl

I have the below code in VHDL that I use in a project. I have been using a Process within the architecture and wanted to know if there were any other means which I'm sure there are of accomplishing the same goal.. in essence to take one number compare it to another and if there is a difference of +/- 2 reflect this in the output. I am using the following:
LIBRARY IEEE;
USE IEEE.std_logic_1164.all, IEEE.std_logic_arith.all, IEEE.std_logic_signed;
ENTITY thermo IS
PORT (
CLK : in std_logic;
Tset, Tact : in std_logic_vector (6 DOWNTO 0);
Heaton : out std_logic
);
END ENTITY thermo;
ARCHITECTURE behavioral OF thermo IS
SIGNAL TsetINT, TactINT : integer RANGE 63 Downto -64; --INT range so no 32bit usage
BEGIN
Heat_on_off: PROCESS
VARIABLE ONOFF: std_logic;
BEGIN
TsetINT <= conv_integer (signed (Tset));--converts vector to Int
TactINT <= conv_integer (signed (Tact));--converts vector to Int
--If you read this why is it conv_integer not to_integer?? thx
ONOFF := '0'; --so variable does not hang on start
WAIT UNTIL CLK'EVENT and CLK = '1';
IF TactINT <= (TsetINT - 2) then
ONOFF := '1';
ELSIF TactINT >= (TsetINT + 2) then
ONOFF := '0';
END IF;
Heaton <= ONOFF;
END PROCESS;
END ARCHITECTURE behavioral;
I'm just after a comparison really and to know if there are any better ways of doing what I have already done.

Why convert Tact and Tset to an integer?
Why have the variable ONOFF? The variable initialization appears to remove any sense of hysteresis, is that what you intended? Based on your other code, I bet not. I recommend that you assign directly to the signal Heaton instead of using the variable ONOFF.
If I were to create TsetINT and TactINt, these would be good candidates to be variables. However, there is no need to do the integer conversion as you can simply do the following:
if signed(Tact) <= signed(Tset) - 2 then
...
elsif signed(Tact) >= signed(Tset) + 2 then
Please use numeric_std. Please ask your professor why they are teaching you old methodologies that are not current industry practice. Numeric_std is an IEEE standard and is updated with the standard, std_logic_arith is not an IEEE standard.
use ieee.numeric_std.all ;

In response to Jim's comment I wrote a simple thermal model test bench to test your design.
I only changed your design to use package numeric_std instead of the Synopsys packages. The rest is just prettifying and eliminating comments not germane to the question of whether or not Tact ever reaches Tset.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity thermo is
port (
CLK: in std_logic;
Tset, Tact: in std_logic_vector (6 downto 0);
Heaton: out std_logic
);
end entity thermo;
architecture behavioral of thermo is
signal TsetINT, TactINT: integer range 63 downto -64;
begin
HEAT_ON_OFF:
process
variable ONOFF: std_logic;
begin
TsetINT <= to_integer (signed (Tset)); -- package numeric_std
TactINT <= to_integer (signed (Tact)); -- instead of conv_integer
ONOFF := '0'; -- AT ISSUE -- so variable does not hang on start
wait until CLK'event and CLK = '1';
if TactINT <= TsetINT - 2 then -- operator precedence needs no parens
ONOFF := '1';
elsif TactINT >= TsetINT + 2 then
ONOFF := '0';
end if;
Heaton <= ONOFF;
end process;
end architecture behavioral;
You have a comment in your process asking why conv_integer was required instead of to_integer. That prompted the change.
I removed superfluous parentheses based on operator order precedence (adding operators being higher precedence than relational operators), notice Jim's answer did the same.
So the simple model thermal model runs with a clock set to a 1 second period, and has two coefficients, relating to the temperature increase when Heaton is '1' or not. I arbitrarily set the heating up coefficient to 1 every 4 clocks, and the temperature decay coefficient to 1 every 10 clocks. Also set the ambient temperature (tout) to 10 and tset to 22. The numbers selected are severe to keep the model run time short enhancing portability without relying on setting a simulator resolution limit.
The thermal model was implemented using fixed signed arithmetic without using fixed_generic_pkg, allowing portability to -1993 tools without math packages and includes a fractional part, responsible for the different widths of Heaton true after reaching normal operating temperature. The model could just as easily have been implemented with two different precursor counters used to tell when to increment or decrement Tact.
Using REAL types is possible, not desirable because converting REAL to INTEGER (then to SIGNED) isn't portable (IEEE Std 1076-2008 Annex D).
The idea here is to demonstrate the lack of hysteresis and demonstrate the model doesn't reach Tset:
The lack of hitting Tset (22 + 2) is based on the lack of hysteresis. Hysteresis is desirable for reducing the number of heat on and off cycles The idea is once you start the heater you leave in on for a while, and once you stop it you want to leave it off for a while too.
Using Jim's modification:
-- signal TsetINT, TactINT: integer range 63 downto -64;
begin
HEAT_ON_OFF:
process (CLK)
begin
if rising_edge(CLK) then
if signed(Tact) <= signed(Tset) - 2 then
Heaton <= '1';
elsif signed(Tact) >= signed(Tset) + 2 then
Heaton <= '0';
end if;
end if;
end process;
gives us longer Heaton on and off cycles, decreasing how many times the heater starts and stops:
And actually allows us to see the temperature reach Tset + 2 as well as Tset - 2. where these thresholds provide the hysteresis which is characterized as a minimum on or minimum off time, depending on the efficiency of the heater and heat loss rate when the heater is off.
So what changed in the execution of the thermo model process? Look at the difference in the synthesis results for the two versions.

Related

Adding large numbers in FPGA in one clock cycle

If I have a VHDL adder which adds two numbers together:
entity adder is
port(
clk : in std_logic;
sync_rst : in std_logic;
signal_A_in : in signed(31 downto 0);
signal_B_in : in signed(31 downto 0);
result_out : out signed(31 downto 0)
);
end adder;
I have two options, one is to concurrently sum signal_A_in and signal_B_in together as so:
architecture rtl of adder is
begin
result_out <= signal_A_in + signal_B_in;
end rtl;
The other is to perform the addition in a clocked process as so:
architecture rtl of adder is
begin
myproc1 : process(clk, sync_rst)
begin
if clk = '1' and clk'event then
if sync_rst='1' then
result_out <= (others=>'0');
else
result_out <= signal_A_in + signal_B_in;
end if;
end if;
end process;
end rtl;
So option B will have a single clock cycle delay compared to option A. However does it guarantee that the result will be ready in one clock cycle (i.e. to meet timing).
The reason I am asking this is because I am getting a timing failure on my design which utilises option A; concurrent summation. I believe that such a methodology is OK for smaller size numbers because the combinatorial logic delay is lower but when the numbers start getting larger the delay increases and the design fails timing. How does the synthesis tool cope with this and does putting the expression in a clocked process solve the issue?
When you write something like signal_A_in + signal_B_in; that is combinatorial logic for an adder. Each FPGA will have different amount of time it takes for signals propagate through wires to+from the adder, and the adder itself.
When you do something like
if clk = '1' and clk'event then
result_out <= signal_A_in + signal_B_in;
As you noted you are now creating a 1 cycle delay by inferring a register.
So now, no matter what your path ends right after your adder sending the result into a register called result_out. Which is why your timing improved. Ex. as shown the path is likely just for your adder - giving you plenty of time and you pass timing. (but be careful adding a register != guaranteed to meet timing).
Timing is worse in your first example and fails because it does not infer a register.
Now not only does your signal need to get across the signal_A_in + signal_B_in adder logic in the clock cycle time - BUT ALSO needs to get across whatever result_out is driving (maybe more adders, other logic somewhere else etc). Your timing path is AT LEAST as long you adder - and probably longer since you didnt break up the path with a register.
Often times even larger adders are done not in 0 cycles (comb. logic) or 1 cycle(with a register output) but over N cycles as a pipelined operation.
This is mostly for you the human to fix - but some synthesis tools can do small retiming of circuits to help.

How to calculate the RPM of a hometrainer with VHDL

I've got a problem; I need to calculate / measure the RPM of a hometrainer using a hall sensor and a magnet on the wheel, the hardware needs to be described in VHDL, my current method is this:
If the hall sensor detects a pulse, reset a counter
Increment counter every clockcycle
On the next pulse, store the previous value, reset, and repeat.
The code:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity teller is
port(
hallsens : in std_logic;
counter : out std_logic_vector(15 downto 0);
areset : in std_logic;
clk : in std_logic
);
end entity teller;
architecture rtl of teller is
signal counttemp : std_logic_vector(15 downto 0);
signal timerval2 : std_logic_vector(15 downto 0);
signal lastcount : std_logic_vector(15 downto 0);
begin
process(clk, areset)
begin
if areset = '1' then
counter <= "0000000000000000";
counttemp <= "0000000000000000";
timerval2 <= "0000001111101000";
elsif hallsens = '1' then
counter <= lastcount + "1";
timerval2 <= "0000001111101000";
counttemp <= "0000000000000000";
elsif rising_edge(clk) then
timerval2 <= timerval2 - "1";
if timerval2 = "0000000000000000" then
lastcount <= counttemp;
counttemp <= counttemp + "1";
timerval2 <= "0000001111101000";
end if;
end if;
end process;
end rtl;
But to calculate the RPM from this I have to divide the counter by the clockspeed, and multiply by 60. This takes up a lot of hardware on the FPGA (Altera Cyclone 2).
Is there a more efficient way to do this?
TIA
I don't have a computer at hand now, but I'll try to point different things I see:
Don't mix numerical libraries (preferably only use the numeric_std) #tricky suggests.
If handling numerical values, and including libraries for that.. you can should use numerical types for signals (integer, unsigned, signed..) it makes things clear and helps to distinguish numeric signals and no numercial-meant signals.
Hallsens is read as a pseudo-reset, but is not in the sensitivity list of the process, this could cause mismatches between Sims and hw. Anyway this is not a good approach, stick with a simple reset and clock pair.
I would detect hallsens within the clocked region of the process and increment the counter of events there. It should be simpler.
I'm assuming your hallsens asserted time is wide enough to be captured by the clock.
Once timer signal has reached zero (I'm assuming this gives you a known time based on your clk frequency) you can reload again the timer (as you do), output the count value and reset the counter, starting again.
For math operations 1/Freq and *60, you could use some numerical tricks if needed, based on the frequency value.. but you could:
multiply by inverse of frequency instead of dividing.
approximate it to sums of power of 2. (60 = 64-4)
make Freq to be multiple of 60 to simplify calcs.
Ps: to be less error prone, you can initialize your vectors (as theyre multiple of 4) in hex format like: signal<=X"0003" avoiding big binary numbers.

Unsigned Addition with Counter Doesn't Work

I'm building a counter that counts rising edges from an input channel. I've simplified my design to include two states, one and two, where counting is done. For some reason, whenever I try to add 1 to counter_reg, or try to assign any number at all to it, the signal becomes red with an X in ModelSim. The code and picture of what happens to the signal are provided below.
I have included the IEEE.NUMERIC_STD.ALL, so I should be able to do unsigned addition. I am not sure what is wrong with counter_reg. Is there anything I'm doing wrong with the counter?
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity photon_counter is
Port ( clk,reset : in STD_LOGIC;
arm,shifter,channel : in STD_LOGIC;
start : in STD_LOGIC);
end photon_counter;
architecture fsm_arch of photon_counter is
type state_type is (idle,zero,one);
type array_type is array (1 downto 0) of UNSIGNED (15 downto 0);
signal state_reg,state_next : state_type;
signal arm_prev,shifter_prev,channel_prev : STD_LOGIC;
signal counter : array_type;
signal counter_reg,counter_next : UNSIGNED (15 downto 0);
begin
--------------------------------------
--State Register
--------------------------------------
process(clk,reset)
begin
if reset='1' then
state_reg <= zero;
counter_reg <= (others => '0');
counter <= (others => (others => '0'));
elsif rising_edge(clk) then
state_reg <= state_next;
counter_reg <= counter_next;
arm_prev <= arm;
shifter_prev <= shifter;
channel_prev <= channel;
end if;
end process;
--------------------------------------
--Next-State Logic/Output Logic
--------------------------------------
process(clk,reset,state_reg,start,counter_reg,shifter_prev,shifter,arm,channel_prev,channel)
begin
--default actions
state_next <= state_reg;
counter_next <= counter_reg;
counter_reg <= counter_reg;
case state_reg is
when idle =>
counter_reg <= (others => '0');
counter <= (others => (others => '0'));
if start = '1' then
state_next <= zero;
end if;
when zero =>
if (shifter = '1') and (shifter_prev = '0') then
state_next <= one;
counter(0) <= counter_reg;
end if;
if (channel = '1') and (channel_prev = '0') then
counter_next <= counter_reg + 1;
end if;
when one =>
if arm = '1' then
state_next <= zero;
counter(1) <= counter_reg;
end if;
if (channel = '1') and (channel_prev = '0') then
counter_reg <= counter_reg + 1;
end if;
end case;
end process;
end fsm_arch;
As shown below, counter_reg and counter_next start off with a value of 0 until I try to add 1 to counter_next. The moment channel_prev rises, both counter_reg and counter_next become X (error) and turn red.
Your counter_reg signal is assigned in two different processes. This is what we call a "multiple drive" situation. It is usually undesirable, just like any short circuit, because when the two processes disagree about the value to assign things are getting very bad.
Solution: drive your counter from one single process.
A bit more about this: if this is bad, why didn't you get an error when compiling or when launching your simulation? Because most people do not know or care about unresolved/resolved types in VHDL. By default, a VHDL type is unresolved. This means that, if you try to drive a signal of this type from more than one process you will get an error at compilation or elaboration time that basically says "I cannot decide what value to assign if your processes disagree, this is forbidden". And this is a very nice feature because such accidental short circuits can have serious consequences. You can try this and see the errors by replacing your unsigned (resolved) counter by a natural (unresolved) one:
signal counter_reg,counter_next : natural 0 to 2**16 - 1;
adapt the rest of your code, and see what happens when compiling.
Sometimes, rarely, it is useful to drive a signal from more than one process (high impedance shared bus, bi-directional RAM data bus...) So VHDL allows to define a resolution function that computes the resulting value of several drivers. This function can be used to define the resolved subtype of a unresolved parent type. If you can find the source code of ieee.std_logic_1164 you will see the declaration of the unresolved, 9-valued std_ulogic type (u for unresolved), the resolution function, and the declaration of the resolved subtype std_logic (see? no u).
But when using resolved types you must yourself take care of not creating short-circuits. There is no compiler error any more, no seatbelt. When one of your driving processes drives a strong value ('0' or '1'), all the others must drive a weak value ('Z' for high impedance). Else, you will get unknown resulting values, represented in red by Modelsim, as you saw.
Unfortunately, most people do not really know what the U stands for in std_Ulogic. So, in order to simplify they always use std_logic instead of what they should use: std_ulogic. Moreover, vendors of logic synthesisers push in the same direction because they frequently favour std_logic (when they do not simply force you to use it). And the people who standardized the ieee.numeric_std package did the same: they declared the unsigned and signed types as resolved types (if fact, they have the same declaration as std_logic_vector). This is as unfortunate as driving full speed, at night, wearing sunglasses, without light and without your seatbelt fasten.
Finally, someone realized how unfortunate this was and the current version of ieee.numeric_std now also declares UNRESOLVED_UNSIGNED (alias U_UNSIGNED) and UNRESOLVED_SIGNED (alias U_SIGNED). Alas, this is a bit too late, most designers will never change their existing code or habits, and I wonder how many bugs could have been avoided if the first choice had been different.
My advices:
never drive a signal from several processes if you do not really intend to have multiple physical drivers, manually avoid short circuits, and so on,
never use a resolved type if you do not need it, so that the tools will raise an error if you accidentally create a multiple drive situation,
in your case, drive counter_reg from one single process, declare it as U_UNSIGNED or NATURAL, and declare all your other signals as STD_ULOGIC, not STD_LOGIC.

How to take samples using fpga?

I want to take samples of digital data coming externaly to FPGA spartan 3.
I want to take 1000 samples/sec initially. How to select a clock frequency in vhdl coding?
Thanks.
Do not use a counter to generate a lower frequency clock signal.
Multiple clock frequencies in an FPGA cause a variety of design problems, some of which come under the heading of "advanced topics" and, while they can (if necessary) all be dealt with and solved, learning how to use a single fast clock is both simpler and generally better practice (synchronous design).
Instead, use whatever fast clock your FPGA board provides, and generate lower frequency timing signals from it, and - crucially - use them as clock enables, not clock signals.
DLLs, DCMs, PLLs and other clock managers do have their uses, but generating 1 kHz clock signals is generally not a good use, even if their limitations permit it. This application is just crying out for a clock enable...
Also, don't mess around with magic numbers, let the VHDL compiler do the work! I have put the timing requirements in a package, so you can share them with the testbench and anything else that needs to use them.
package timing is
-- Change the first two constants to match your system requirements...
constant Clock_Freq : real := 40.0E6;
constant Sample_Rate : real := 1000.0;
-- These are calculated from the above, so stay correct when you make changes
constant Divide : natural := natural(Clock_Freq / Sample_Rate);
-- sometimes you also need a period, e.g. in a testbench.
constant clock_period : time := 1 sec / Clock_Freq;
end package timing;
And we can write the sampler as follows:
(I have split the clock enable out into a separate process to clarify the use of clock enables, but the two processes could be easily rolled into one for some further simplification; the "sample" signal would then be unnecessary)
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.numeric_std.all;
use work.timing.all;
entity sampler is
Port (
Clock : in std_logic;
Reset : in std_logic;
ADC_In : in signed(7 downto 0);
-- signed for audio, or unsigned, depending on your app
Sampled : out signed(7 downto 0);
);
end sampler;
architecture Behavioral of Sampler is
signal Sample : std_logic;
begin
Gen_Sample : process (Clock,Reset)
variable Count : natural;
begin
if reset = '1' then
Sample <= '0';
Count := 0;
elsif rising_edge(Clock) then
Sample <= '0';
Count := Count + 1;
if Count = Divide then
Sample <= '1';
Count := 0;
end if;
end if;
end process;
Sample_Data : process (Clock)
begin
if rising_edge(Clock) then
if Sample = '1' then
Sampled <= ADC_In;
end if;
end if;
end process;
end Behavioral;
The base clock must be based on an external clock, and can't be generated just through internal resources in a Spartan-3 FPGA. If required, you can use the Spartan-3 FPGA Digital Clock Manager (DCM) resources to scale the external clock. Synthesized VHDL code in itself can't generate a clock.
Once you have some base clock at a higher frequency, for example 100 MHz, you can easily divide this down to generate an indication at 1 kHz for sampling of the external input.
It depends on what clock frequency you have available. If you have a 20MHz clock source, you need to divided it by 20000 in order to get 1KHz, you can do it in VHDL or use a DCM to do this.
This is from an example on how to create a 1kHz clock from a 20MHz input:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity clk20Hz is
Port (
clk_in : in STD_LOGIC;
reset : in STD_LOGIC;
clk_out: out STD_LOGIC
);
end clk200Hz;
architecture Behavioral of clk20Hz is
signal temporal: STD_LOGIC;
signal counter : integer range 0 to 10000 := 0;
begin
frequency_divider: process (reset, clk_in) begin
if (reset = '1') then
temporal <= '0';
counter <= 0;
elsif rising_edge(clk_in) then
if (counter = 10000) then
temporal <= NOT(temporal);
counter <= 0;
else
counter <= counter + 1;
end if;
end if;
end process;
clk_out <= temporal;
end Behavioral;

Mod-M counter Unsigned values have no signal

I am writing a RS232 module for my Nexys2 board. I am currently having issues with my baud rate controller which I want to set to 19200.
For this I am using a Mod-M counter, after many ISim simulations the problem with my code is in the mod-m counter as it is not producing any ticks.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
use IEEE.NUMERIC_STD.ALL;
-- Uncomment the following library declaration if instantiating
-- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;
entity baud_rate is
generic (
N: integer := 8;
M: integer :=163);
Port (clk, reset : in STD_LOGIC;
tick : out STD_LOGIC;
q : out STD_LOGIC_VECTOR(N-1 downto 0));
end baud_rate;
architecture Behavioral of baud_rate is
signal r_reg : unsigned(N-1 downto 0);
signal r_next : unsigned(N-1 downto 0);
begin
process(clk,reset)
begin
if (reset ='1') then
r_reg <= (others=>'0');
elsif(clk'event and clk='1') then
r_reg <= r_next;
end if;
end process;
r_next <= (others =>'0') when r_reg=(M-1) else r_reg+1;
tick <='1' when r_reg=(M-1) else '0';
q <= std_logic_vector(r_reg);
end Behavioral;
I have tested and all the clk inputs and run fine and the issue seems to be with the r_reg and r_next registers. In ISim when outputing either of these on q I get UUUUUUUU, so it seems they are not generating signal. From this i can infer that the two r_reg and r_next registers aren't being created or storing values, is there an issue when using unsigned?
To make triple sure I have even copied the mod-m counter from the book FPGA Prototyping with VHDL (which is the code shown) BUT still this does not work and q output is UUUUUUUU.
If there are any better ways of creating a baud rate from the nexys2 50mz clock that would also be appreciated!
Cheers
Frankly I am horrified if people are expected to learn VHDL from a book where examples like this are presented. I know the author has a similar book on Verilog : do people end up thinking VHDL is just a more verbose Verilog?
Specific criticisms (actually 7,8 are more observations):
1) Spurious type conversions.
Q represents an unsigned number. So make it unsigned!
The baud generator isn't the only thing in your FPGA so Q isn't likely to be an off-chip port. There are good arguments for making top level, off-chip ports std_logic_vector but even that isn't compulsory. However, if your customer's specification or coding style insists on spurious type conversions on ports; follow it.
2) the DRY principle:
package CPU_types is
subtype baud_count is unsigned(7 downto 0);
end CPU_types;
Spot the simplification in maintenance.
If you are using a subtype in several places, put it in a package; the universal code reuse tool.
3) Indentation, formatting. (I recognise that may have become garbled by editor settings). It adds to the brain load reading it. What I've done here isn't The One Way though.
4) Spurious brackets round logical expressions. Harmless, but look like crutches for C programmers.
5) Antique clk'event style. Next year, the rising_edge function will be old enough to drink (in America. In Britain it's been getting plastered every Saturday night for a couple of years now...)
6) The "two process" style with r_reg and r_next. Does he also write state machines with a separate combinational process on next_state? Given this, I'm guessing so. Single process state machines are easier, smaller (to write : they don't generate smaller hardware) and safer.
7) I cheated and my tick is one cycle later than in the original. If that is critical, restore the external "tick" assignment. I also made it synchronous vhich will help performance. Some people would prefer tick <= '0' in an else clause; however the default assignment I used is safe, and prevents a lot of mistakes (and unnecessary else clauses) in larger designs.
8) The assignment to Q can be brought into the process too; if you made r_reg a process variable you'd have to. There is room for other variations and preferences.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use CPU_types.all;
entity baud_rate is
generic (
M: integer := 163);
Port (
clk, reset : in STD_LOGIC;
tick : out STD_LOGIC;
q : out baud_count);
end baud_rate;
architecture Behavioral of baud_rate is
signal r_reg : baud_count;
begin
process(clk,reset)
begin
if reset ='1' then
r_reg <= (others=>'0');
elsif rising_edge(clk) then
tick <= 0;
r_reg <= r_reg+1;
if r_reg = M then
tick <= '1';
r_reg <= (others=>'0');
end if;
end if;
end process;
-- tick <='1' when r_reg = M-1 else '0';
-- or simpler, when r_reg = 0
q <= r_reg;
end Behavioral;

Resources