I am unfortunately new to VHDL but not new to software development. What is the equivalency to functions in VHDL? Specifically, in the code below I need to debounce four push buttons instead of one. Obviously repeating my process code four times and suffixing each of my signals with a number to make them unique for the four instances is not the professional nor correct way of doing this. How do I collapse all this down into one process "function" to which I can "pass" the signals so I can excise all this duplicate code?
----------------------------------------------------------------------------------
-- Debounced pushbutton examples
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity pushbutton is
generic(
counter_size : integer := 19 -- counter size (19 bits gives 10.5ms with 50MHz clock)
);
port(
CLK : in std_logic; -- input clock
BTN : in std_logic_vector(0 to 3); -- input buttons
AN : out std_logic_vector(0 to 3); -- 7-segment digit anodes ports
LED : out std_logic_vector(0 to 3) -- LEDs
);
end pushbutton;
architecture pb of pushbutton is
signal flipflops0 : std_logic_vector(1 downto 0); -- input flip flops
signal flipflops1 : std_logic_vector(1 downto 0);
signal flipflops2 : std_logic_vector(1 downto 0);
signal flipflops3 : std_logic_vector(1 downto 0);
signal counter_set0 : std_logic; -- sync reset to zero
signal counter_set1 : std_logic;
signal counter_set2 : std_logic;
signal counter_set3 : std_logic;
signal counter_out0 : std_logic_vector(counter_size downto 0) := (others => '0'); -- counter output
signal counter_out1 : std_logic_vector(counter_size downto 0) := (others => '0');
signal counter_out2 : std_logic_vector(counter_size downto 0) := (others => '0');
signal counter_out3 : std_logic_vector(counter_size downto 0) := (others => '0');
signal button0 : std_logic; -- debounce input
signal button1 : std_logic;
signal button2 : std_logic;
signal button3 : std_logic;
signal result0 : std_logic; -- debounced signal
signal result1 : std_logic;
signal result2 : std_logic;
signal result3 : std_logic;
begin
-- Make sure Mercury BaseBoard 7-Seg Display is disabled (anodes are pulled high)
AN <= (others => '1');
-- Feed buttons into debouncers
button0 <= BTN(0);
button1 <= BTN(1);
button2 <= BTN(2);
button3 <= BTN(3);
-- Start or reset the counter at the right time
counter_set0 <= flipflops0(0) xor flipflops0(1);
counter_set1 <= flipflops1(0) xor flipflops1(1);
counter_set2 <= flipflops2(0) xor flipflops2(1);
counter_set3 <= flipflops3(0) xor flipflops3(1);
-- Feed LEDs from the debounce circuitry
LED(0) <= result0;
LED(1) <= result1;
LED(2) <= result2;
LED(3) <= result3;
-- Debounce circuit 0
process (CLK)
begin
if (CLK'EVENT and CLK = '1') then
flipflops0(0) <= button0;
flipflops0(1) <= flipflops0(0);
if (counter_set0 = '1') then -- reset counter because input is changing
counter_out0 <= (others => '0');
elsif (counter_out0(counter_size) = '0') then -- stable input time is not yet met
counter_out0 <= counter_out0 + 1;
else -- stable input time is met
result0 <= flipflops0(1);
end if;
end if;
end process;
-- Debounce circuit 1
process (CLK)
begin
if (CLK'EVENT and CLK = '1') then
flipflops1(0) <= button1;
flipflops1(1) <= flipflops1(0);
if (counter_set1 = '1') then -- reset counter because input is changing
counter_out1 <= (others => '0');
elsif (counter_out1(counter_size) = '0') then -- stable input time is not yet met
counter_out1 <= counter_out1 + 1;
else -- stable input time is met
result1 <= flipflops1(1);
end if;
end if;
end process;
-- Debounce circuit 2
process (CLK)
begin
if (CLK'EVENT and CLK = '1') then
flipflops2(0) <= button2;
flipflops2(1) <= flipflops2(0);
if (counter_set2 = '1') then -- reset counter because input is changing
counter_out2 <= (others => '0');
elsif (counter_out2(counter_size) = '0') then -- stable input time is not yet met
counter_out2 <= counter_out2 + 1;
else -- stable input time is met
result2 <= flipflops2(1);
end if;
end if;
end process;
-- Debounce circuit 3
process (CLK)
begin
if (CLK'EVENT and CLK = '1') then
flipflops3(0) <= button3;
flipflops3(1) <= flipflops3(0);
if (counter_set3 = '1') then -- reset counter because input is changing
counter_out3 <= (others => '0');
elsif (counter_out3(counter_size) = '0') then -- stable input time is not yet met
counter_out3 <= counter_out3 + 1;
else -- stable input time is met
result3 <= flipflops3(1);
end if;
end if;
end process;
end pb;
VHDL has functions but function calls are expressions and not statements or expression statements as in some programming languages. A function call always returns a value of a type and an expression can't represent a portion of a design hierarchy.
Consider the other subprogram class procedures which are statements instead.
The debouncer processes and associated declarations can also be simplified without using a procedure:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity pushbutton is
generic (
counter_size: integer := 19 -- The left bound of debounce counters
);
port (
clk: in std_logic;
btn: in std_logic_vector(0 to 3);
an: out std_logic_vector(0 to 3);
led: out std_logic_vector(0 to 3)
);
end entity pushbutton;
architecture pb1 of pushbutton is
-- There are two flip flops for each of four buttons:
subtype buttons is std_logic_vector(0 to 3);
type flip_flops is array (0 to 1) of buttons;
signal flipflops: flip_flops;
signal counter_set: std_logic_vector(0 to 3);
use ieee.numeric_std.all;
type counter is array (0 to 3) of
unsigned(counter_size downto 0);
signal counter_out: counter := (others => (others => '0'));
begin
an <= (others => '1');
counter_set <= flipflops(0) xor flipflops(1);
DEBOUNCE:
process (clk)
begin
if rising_edge (clk) then
flipflops(0) <= btn;
flipflops(1) <= flipflops(0);
for i in 0 to 3 loop
if counter_set(i) = '1' then
counter_out(i) <= (others => '0');
elsif counter_out(i)(counter_size) = '0' then
counter_out(i) <= counter_out(i) + 1;
else
led(i) <= flipflops(1)(i);
end if;
end loop;
end if;
end process;
end architecture pb1;
Moving part of the design specification into a procedure:
architecture pb2 of pushbutton is
-- There are two flip flops for each of four buttons:
subtype buttons is std_logic_vector(0 to 3);
type flip_flops is array (0 to 1) of buttons;
signal flipflops: flip_flops;
signal counter_set: std_logic_vector(0 to 3);
use ieee.numeric_std.all;
type counter is array (0 to 3) of
unsigned(counter_size downto 0);
signal counter_out: counter := (others => (others => '0'));
procedure debounce (
-- Can eliminate formals of mode IN within the scope of their declaration:
-- signal counter_set: in std_logic_vector (0 to 3);
-- signal flipflops: in flip_flops;
signal counter_out: inout counter;
signal led: out std_logic_vector(0 to 3)
) is
begin
for i in 0 to 3 loop
if counter_set(i) = '1' then
counter_out(i) <= (others => '0');
elsif counter_out(i)(counter_size) = '0' then
counter_out(i) <= counter_out(i) + 1;
else
led(i) <= flipflops(1)(i);
end if;
end loop;
end procedure;
begin
an <= (others => '1');
counter_set <= flipflops(0) xor flipflops(1);
DEBOUNCER:
process (clk)
begin
if rising_edge (clk) then
flipflops(0) <= btn;
flipflops(1) <= flipflops(0);
-- debounce(counter_set, flipflops, counter_out, led);
debounce (counter_out, led);
end if;
end process;
end architecture pb2;
Here the procedure serves as a collection of sequential statements and doesn't save any lines of code.
Sequential procedure calls can be useful to hide repetitious clutter. The clutter has been removed already by consolidating declarations and using the loop statement. There's a balancing act between the design entry effort, code maintenance effort and user readability, which can also be affected by coding style. Coding style is also affected by RTL constructs implying hardware.
Moving the clock evaluation into a procedure would require the procedure call be be a concurrent statement, similar to an instantiation, which you already have. It doesn't seem worthwhile here should you consolidate signals declared as block declarative items in the architecture body or when using a loop statement.
Note that result and button declarations have been eliminated. Also the use of package numeric_std and type unsigned for the counters prevents inadvertent assignment to other objects with the same subtype. The counter values are treated as unsigned numbers while counter_set for instance is not.
Also there's an independent counter for each input being debounced just as in the original. Without independent counters some events might be lost for independent inputs when a single counter is repetitively cleared.
This code hasn't been validated by simulation, lacking a testbench. With the entity both architectures analyze and elaborate.
There doesn't appear to be anything here other than sequential statements now found in a for loop that would benefit from a function call. Because a function call returns a value the type of that value would either need to be a composite (here a record type) or be split into separate function calls for each assignment target.
There's also the generate statement which can elaborate zero or more copies of declarations and concurrent statements (here a process) as block statements with block declarative items. Any signal used only in an elaborated block can be a block declarative item.
architecture pb3 of pushbutton is
begin
DEBOUNCERS:
for i in btn'range generate
signal flipflops: std_logic_vector (0 to 1);
signal counter_set: std_logic;
signal counter_out: unsigned (counter_size downto 0) :=
(others => '0');
begin
counter_set <= flipflops(0) xor flipflops(1);
DEBOUNCE:
process (clk)
begin
if rising_edge (clk) then
flipflops(0) <= btn(i);
flipflops(1) <= flipflops(0);
if counter_set = '1' then
counter_out <= (others => '0');
elsif counter_out(counter_size) = '0' then
counter_out <= counter_out + 1;
else
led(i) <= flipflops(1);
end if;
end if;
end process;
end generate;
end architecture pb3;
Addendum
The OP pointed out an error made in the above code due to a lack of simulation and complexity hidden by abstraction when synthesizing architecture pb2. While the time for the debounce counter was given at 10.5 ms (50 MHz clock) the name of the generic (counter_size) is also actually the left bound of the counter (given as an unsigned binary counter using type unsigned).
The mistake (two flip flops in the synchronizer for each of four buttons) and simply acceding to the OP's naming convention with respect to the counter has been corrected in the above code.
The OP's synthesis error in the comment relates to the requirement there be a matching element for each element on the left hand or right hand of an aassignment statement.
Without synthesizing the code (which the OP did) the error can't be found without simulation. Because the only thing necessary to find the particular error assigning flipflops(0) is the clock a simple testbench can be written:
use ieee.std_logic_1164.all;
entity pushbutton_tb is
end entity;
architecture fum of pushbutton_tb is
signal clk: std_logic := '0';
signal btn: std_logic_vector (0 to 3);
signal an: std_logic_vector(0 to 3);
signal led: std_logic_vector(0 to 3);
begin
CLOCK:
process
begin
wait for 0.5 ms;
clk <= not clk;
if now > 50 ms then
wait;
end if;
end process;
DUT:
entity work.pushbutton (pb2)
generic map (
counter_size => 4 -- FOR SIMULATION
)
port map (
clk => clk,
btn => btn,
an => an,
led => led
);
STIMULUS:
process
begin
btn <= (others => '0');
wait for 20 ms;
btn(0) <= '1';
wait for 2 ms;
btn(1) <= '1';
wait for 3 ms;
btn(2) <= '1';
wait for 6 ms;
btn(3) <= '1';
wait;
end process;
end architecture;
The corrected code and a testbench to demonstrate there are no matching element errors in assignment during simulation.
Simulation was provided for both architectures with identical results.
The generic was used to reduce the size of the debounce counters using a 1 millisecond clock in the testbench (to avoid simulation time with 50 MHz clock events that don't add to the narrative).
Here's the output of the first architecture's simulation:
The caution here is that designs should be simulated. There's a class of VHDL semantic error conditions that are checked only at runtime (or in synthesis).
Added abstraction for reducing 'uniquified' code otherwise identically performing can introduce such errors.
The generate statement wouldn't have that issue using names in a design hierarchy:
The concurrent statements and declarations found in a generate statement are replicated in any generated block statements implied by the generate statement. Each block statement represents a portion of a design hierarchy.
There's been a trade off between design complexity and waveform display organization for debugging.
A design description depending on hiding repetitious detail should be simulated anyway. Here there are two references to the generate parameter i used in selected names, susceptible to the same errors as ranges should parameter substitution be overlooked.
A multiple bit debouncing circuit might look like this:
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use work.Utilities.all;
entity Debouncer is
generic (
CLOCK_PERIOD_NS : positive := 10;
DEBOUNCE_TIME_MS : positive := 3;
BITS : positive
);
port (
Clock : in std_logic;
Input : in std_logic_vector(BITS - 1 downto 0);
Output : out std_logic_vector(BITS - 1 downto 0) := (others => '0')
);
end entity;
architecture rtl of Debouncer is
begin
genBits: for i in Input'range generate
constant DEBOUNCE_COUNTER_MAX : positive := (DEBOUNCE_TIME_MS * 1000000) / CLOCK_PERIOD_NS;
constant DEBOUNCE_COUNTER_BITS : positive := log2(DEBOUNCE_COUNTER_MAX);
signal DebounceCounter : signed(DEBOUNCE_COUNTER_BITS downto 0) := to_signed(DEBOUNCE_COUNTER_MAX - 3, DEBOUNCE_COUNTER_BITS + 1);
begin
process (Clock)
begin
if rising_edge(Clock) then
-- restart counter, whenever Input(i) was unstable within DEBOUNCE_TIME_MS
if (Input(i) /= Output(i)) then
DebounceCounter <= DebounceCounter - 1;
else
DebounceCounter <= to_signed(DEBOUNCE_COUNTER_MAX - 3, DebounceCounter'length);
end if;
-- latch input bit, if input was stable for DEBOUNCE_TIME_MS
if (DebounceCounter(DebounceCounter'high) = '1') then
Output(i) <= Input(i);
end if;
end if;
end process;
end generate;
end architecture;
In stead of a counter size, it expects the user to provide a frequency (as period in nanoseconds) and a debounce time (in milliseconds).
The referenced package implements a log2 function.
Related
I have the VHDL implementation that works on board, it detects the sequence 01110 and will raise a flag for 2 clock counts. It detects overlapping sequences as well where 011101110 would raise the flag twice.
I've checked my implementation with a logic analyzer on the board and am fairly confident that it works. I am feeding in a repetition sequence of 0111 at 10 kHz, on the board, it has a clock at 100 MHz where I scale it to 10 kHz with a prescaler.
My problem is, when trying to recreate a similar scenario using a simulation, I do not get any outputs as expected
Image from logic analyzer from board
Image from Test Bench
Test Bench Code
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity test_FSM_prac4 is
-- Port ( );
end test_FSM_prac4;
architecture Behavioral of test_FSM_prac4 is
component FSM_prac4 is
port (
inputSignal : in STD_LOGIC;
pushButton : in STD_LOGIC;
clk100mhz : in STD_LOGIC;
logic_analyzer : out STD_LOGIC_VECTOR (7 downto 0);
LEDs: out STD_LOGIC
); end component;
signal inputSignal : std_logic := '0';
signal pushButton: std_logic := '0';
signal clk100mhz: std_logic := '0';
signal logic_analyzer: std_logic_vector(7 downto 0);
signal LEDs : std_logic;
begin
uut : FSM_prac4 port map(
inputSignal => inputSignal,
pushButton => pushButton,
clk100mhz => clk100mhz,
logic_analyzer => logic_analyzer,
LEDs => LEDs
);
--generate clock 100mhz
clock_tic: process begin
loop
clk100mhz <= '0';
wait for 5ns;
clk100mhz <= '1';
wait for 5ns;
end loop;
end process;
input_changes: process begin
loop
inputSignal <= '0';
wait for 100us;
inputSignal <= '1';
wait for 100us;
inputSignal <= '1';
wait for 100us;
inputSignal <= '1';
wait for 100us;
end loop;
end process;
end Behavioral;
To show the mapping for logic Analyzer
logic_analyzer(0) <= masterReset;
logic_analyzer(1) <= newClock -- 10Khz Clock;
logic_analyzer(2) <= outputZ;
--FSM States
logic_analyzer(3) <= '1' when y = A ELSE '0';
logic_analyzer(4) <= '1' when y = B ELSE '0';
logic_analyzer(5) <= '1' when y = C ELSE '0';
logic_analyzer(6) <= '1' when y = D ELSE '0';
logic_analyzer(7) <= '1' when y = E ELSE '0';
If anyone could direct to what I am doing wrong on the test bench and how to replicate to get similar results as the first image as it shows that in simulation, it always stays at state A and the new clock is not toggling meaning that clk100mhz is somehow not connected but I can't figure out why.
Any help is greatly appreciated, thanks guys
edit:
I wrote a simple program to test my scalar clock
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity scaler_clk is
Port (
pushButton : in std_logic;
indicator : out std_logic;
clk100mhz : in STD_LOGIC;
clk10khz: out STD_LOGIC
);
end scaler_clk;
architecture Behavioral of scaler_clk is
signal clockScalers : std_logic_vector (12 downto 0):= (others => '0') ;
signal prescaler: std_logic_vector(12 downto 0) := "1001110001000";
signal newClock: std_logic := '0';
signal masterReset : std_logic;
begin
clk10khz <= newClock;
masterReset <= pushButton;
process (clk100mhz,masterReset) begin
if(masterReset <= '1') then <--- error occurs here
clockScalers <= "0000000000000";
newClock <= '0';
indicator <= '1';
elsif (clk100mhz'event and clk100mhz = '1')then
indicator <= '0';
clockScalers <= clockScalers + 1;
if(clockScalers > prescaler) then
newClock <= not newClock;
clockScalers <= (others => '0');
end if;
end if;
end process;
end Behavioral;
test bench code
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity test_scaler_clk is
-- Port ( );
end test_scaler_clk;
architecture Behavioral of test_scaler_clk is
component scaler_clk Port (
pushButton : in std_logic;
indicator : out std_logic;
--input clock
clk100mhz : in STD_LOGIC;
clk10khz: out STD_LOGIC
);end component;
signal clk100mhz: std_logic := '0';
signal clk10khz : std_logic;
signal pushButton: std_logic;
signal indicator : std_logic;
begin
uut: scaler_clk port map(
pushButton => pushButton,
indicator => indicator,
clk100mhz => clk100mhz,
clk10khz => clk10khz
);
pushButton <= '0';
clock_tic: process begin
loop
clk100mhz <= '0';
wait for 5ns;
clk100mhz <= '1';
wait for 5ns;
end loop;
end process;
end Behavioral;
Even though I set pushButton to '0', it is still triggering masterReset, anyone knows why, that's why the 10 kHz clock isn't working
There are several things that you could (should) improve in your code. As Brian already explained, in your Behavioral architecture of scaler_clk, you should have:
if(masterReset = '1') then
instead of:
if(masterReset <= '1') then
Now, let's start with the most likely cause of your initial problem: unbound components. Your test benches instantiate the design to validate as components. VHDL components are kind of prototypes of actual entities. Prototypes are enough to compile because the compiler can perform all necessary syntax and type checking. But they are not enough to simulate because the simulator also needs the implementation behind the prototype. Some tools have a default binding strategy for unbound components: if they find an entity with the same name and if it has only one architecture, they use that. Your simulator apparently does not use such strategy (at least not by default, there is maybe an option for that but it is disabled). Note that most simulators I know issue warnings when they find unbound components. You probably missed these warnings.
Anyway, your component instances are unbound (they have no associated entity/architecture) and the simulator considers them as black boxes. Their outputs are not driven, except by the initial values you declared (1).
How to fix this? Two options:
Use a configuration to specify which entity/architecture pair shall be used for each component instance:
for all: scaler_clk use entity work.scaler_clk(Behavioral);
Use entity instantiations instead of components:
uut: entity work.scaler_clk(Behavioral) port map...
Now, let's go through some other aspects of your code that could be improved:
You are using non-standard packages, that are frequently not even compatible: IEEE.STD_LOGIC_ARITH and IEEE.STD_LOGIC_UNSIGNED. As they are not standard they should not even be in the standard IEEE library. You should use IEEE.NUMERIC_STD instead, and only that one. It declares the SIGNED and UNSIGNED types (with the same declaration as STD_LOGIC_VECTOR) and overloads the arithmetic operators on them.
Your test benches generate the 100MHz clock with:
clock_tic: process begin
loop
clk100mhz <= '0';
wait for 5ns;
clk100mhz <= '1';
wait for 5ns;
end loop;
end process;
The infinite loop is useless: a process is already an infinite loop:
clock_tic: process
begin
clk100mhz <= '0';
wait for 5ns;
clk100mhz <= '1';
wait for 5ns;
end process clock_tic;
would do the same. Same remark for your input_changes process.
Your input_changes process uses wait for <duration> statements. This is not a good idea because you do not know when the inputSignal signal toggles, compared to the clock. Is it just before, just after or exactly at the same time as the rising edge of clk100mhz? And if it is exactly at the same time, what will happen? Of course, you can carefully chose the <durations> to avoid such ambiguities but it is error prone. You should use the wait for <duration> only in the clock generating process. Everywhere else, it is better to synchronize with the clock:
input_changes: process
begin
inputSignal <= '0';
for i in 1 to 10000 loop
wait until rising_edge(clk100mhz);
end loop;
inputSignal <= '1';
for i in 1 to 10000 loop
wait until rising_edge(clk100mhz);
end loop;
inputSignal <= '1';
for i in 1 to 10000 loop
wait until rising_edge(clk100mhz);
end loop;
inputSignal <= '1';
for i in 1 to 10000 loop
wait until rising_edge(clk100mhz);
end loop;
end process input_changes;
This guarantees that inputSignal changes just after the rising edge of the clock. And you could rewrite it in a bit more elegant way (and probably a bit easier to maintain):
input_changes: process
constant values: std_logic_vector(0 to 3) := "0111";
begin
for i in values'range loop
inputSignal <= values(i);
for i in 1 to 10000 loop
wait until rising_edge(clk100mhz);
end loop;
end loop;
end process input_changes;
You are using resolved types (STD_LOGIC and STD_LOGIC_VECTOR). These types allow multiple drive, that is, having a hardware wire (VHDL signal) that is driven by several devices (VHDL processes). Usually you do not want this. Usually you even want to avoid this like the plague because it can cause short-circuits. In most cases it is wiser to use non-resolved types (STD_ULOGIC and STD_ULOGIC_VECTOR) because the compiler and/or the simulator will raise errors if you accidentally create a short circuit in your design.
One last thing: if, as its name suggests, you intend to use the clk10khz signal as a real clock, you should reconsider this decision. It is a signal that you generate with your custom logic. Clocks have very specific electrical and timing constraints that cannot really be fulfilled by regular signals. Before using clk10khz as a clock you must deal with clock skew, clock buffering... Not impossible but tricky. If you did use it as a clock your synthesizer probably issued critical warnings that you also missed (have a look maybe at the timing report). Moreover, this is probably useless in your case: an enable signal generated from clk100mhz could probably be used instead, avoiding all these problems. Instead of:
process (clk100mhz,masterReset) begin
if(masterReset = '1') then
clockScalers <= "0000000000000";
newClock <= '0';
indicator <= '1';
elsif (clk100mhz'event and clk100mhz = '1')then
indicator <= '0';
clockScalers <= clockScalers + 1;
if(clockScalers > prescaler) then
newClock <= not newClock;
clockScalers <= (others => '0');
end if;
end if;
end process;
use:
signal tick10khz: std_ulogic;
...
process(clk100mhz, masterReset) begin
if masterReset = '1') then
clockScalers <= "0000000000000";
tick10khz <= '0';
elsif rising_edge(clk100mhz) then
clockScalers <= clockScalers + 1;
tick10khz <= '0'
if(clockScalers > prescaler) then
tick10khz <= '1';
clockScalers <= (others => '0');
end if;
end if;
end process;
And then, instead of:
process(clk10khz)
begin
if rising_edge(clk10khz) then
register <= register_input;
end if;
end process;
use:
process(clk100mhz)
begin
if rising_edge(clk100mhz) then
if tick10khz = '1' then
register <= register_input;
end if;
end if;
end process;
The result will be the same but with only one single 100MHz clock, which avoids clock skew, clock buffering and clock domain crossing problems.
(1) This illustrates why declaring variables and signals with initial values is usually not a good idea: it hides potential problems. Without this your signals would have been stuck at 'U' (uninitialized) and it would maybe have helped understanding where the problem comes from.
Hi, i'm coding the main program structure for my LCM, called DE2_LCM2(in vhdl).
Within the main structure, there is a clock divider calls PLL2 (in verilog) and a I2S_LCM_Config (in verilog).
My PLL2.v and I2S_LCM.v are given by vendor, except for my DE2_LCM.vhd, myself coded. Compile successful, but stimulation failed.
FYI:
Horizontal scan:
1 Horizontal Line, there are 1171 counts or cycles of DCLK. The LCM_HSYNC goes low during falling edge of DCLK for 1 cycle. For the first 152 cycle, the data on LCM_DATA bus are invalid, start valid from cycle 153 to 1112, and invalid from cycle 1112 to 1171.
Vertical scan (Non-interlace):
After the last cycle of a horizontal line, the vertical counter shall be incremented by one. This LCM got 262 vertical lines in total, but only line 15 to (15+240)=255 is displayed.
LCM_PLL.v:
This file helps to convert system clock 50MHz to 18.42MHz. the DCLK or LCM_DCLK will be used for horizontal and vertical counter.
Below is my DE2_LCM.vhd codes, can't find what is going wrong on my code. Some more my teacher is on leave.
library ieee;
use ieee.std_logic_1164.all;
use IEEE.std_logic_unsigned.all;
entity DE2_LCM2 is port(CLOCK_50 : in std_logic;
KEY0 : in std_logic;
SW : in std_logic_vector(1 downto 0);
LCM_DATA : out std_logic_vector(7 downto 0);
LCM_DCLK, LCM_HSYNC, LCM_VSYNC,LCM_SCLK,LCM_SDAT,LCM_SCEN,LCM_GRST,LCM_SHDB : out std_logic
);
end DE2_LCM2;
architecture rtl of DE2_LCM2 is
constant H_SYNC_CYC: integer:=1;
constant H_SYNC_BACK: integer:=152;
constant H_SYNC_ACT: integer:=960;
constant H_SYNC_FRONT: integer:=59;
constant H_SYNC_TOTAL: integer:=1171;
constant V_SYNC_CYC: integer:=1;
constant V_SYNC_BACK: integer:=14;
constant V_SYNC_ACT: integer:=240;
constant V_SYNC_FRONT: integer:=8;
constant V_SYNC_TOTAL: integer:=262;
signal H_Cont: std_logic_vector(10 downto 0);
signal V_Cont: std_logic_vector(10 downto 0);
signal MOD_CNT: std_logic_vector(1 downto 0);
signal Tmp_DATA1: std_logic_vector(11 downto 0);
signal CLK_18: std_logic;
signal mSEL: std_logic_vector(1 downto 0);
signal iRST_N: std_logic;
signal I2S_SDAT: std_logic;
component LCM_PLL2 port(inclk0: in std_logic;
c0: out std_logic);
end component;
component I2S_LCM_Config is port(iCLK: in std_logic;
iRST_N: in std_logic;
I2S_SCLK: out std_logic;
I2S_SDAT: inout std_logic;
I2S_SCEN: out std_logic);
end component;
begin
LCM_GRST<=KEY0;
LCM_DCLK<=not(CLK_18);
LCM_SHDB<='1';
iRST_N<=KEY0;
LCM_SDAT<=I2S_SDAT; --add on
process(SW,MOD_CNT )
begin
if(SW="00")then
if(MOD_CNT="00")then
LCM_DATA<="01111111";
else LCM_DATA<="00000000";
end if;
elsif(SW="01")then
if(MOD_CNT="01")then
LCM_DATA<="01111111";
else LCM_DATA<="00000000";
end if;
elsif(SW="10")then
if(MOD_CNT="10")then
LCM_DATA<="01111111";
else LCM_DATA<="00000000";
end if;
else LCM_DATA<="00000000";
end if;
end process;
u0:LCM_PLL2 port map(inclk0=>CLOCK_50,
c0=>CLK_18);
u1:I2S_LCM_Config port map(iCLK=>CLOCK_50,
iRST_N=>KEY0,
I2S_SCLK=>LCM_SCLK,
I2S_SDAT=>I2S_SDAT,
I2S_SCEN=>LCM_SCEN);
process(CLK_18,iRST_N)
begin
if(rising_edge(CLK_18))then
if iRST_N = '0'then
MOD_CNT <= "11";
H_Cont <= "00000000000";
LCM_HSYNC <= '0';
V_Cont <= "00000000000";
LCM_VSYNC <= '0';
else
if((H_Cont >= H_SYNC_BACK) and (H_Cont<(H_SYNC_TOTAL-H_SYNC_FRONT)))then
if(MOD_CNT < "10") then
MOD_CNT <= MOD_CNT + '1';
else
MOD_CNT <= "00";
end if;
else MOD_CNT <= "11";
end if;
if(H_Cont < (H_SYNC_TOTAL-1)) then
H_Cont <= H_Cont + '1';
else H_cont <= "00000000000";
end if;
if(H_Cont < H_SYNC_CYC)then
LCM_HSYNC <= '0';
else LCM_HSYNC <= '1';
end if;
if(V_Cont <(V_SYNC_TOTAL-1)) then
V_Cont <= V_Cont+'1';
else V_Cont <= "00000000000";
end if;
if(V_Cont < V_SYNC_CYC) then
LCM_VSYNC <= '0';
else LCM_VSYNC <= '1';
end if;
end if;
end if;
end process;
end rtl;
Should be my coding style that is not suitable for hardware programming. Do let me know if PLL2.v and I2S_LCM_Config.v are needed for your testing. I'll send u through email.
Thanks in advance:)
Without checking your PLL or I2C, the vertical counter appears to be counting clocks and not lines:
There needs to be an additional qualifier to only increment V_cont when H_Cont is the maximum count (1170).
Something along the lines of:
if H_Cont = std_logic_vector (to_unsigned (H_SYNC_TOTAL-1,11)) then
if V_Cont < std_logic_vector(to_unsigned (V_SYNC_TOTAL-1, 11)) then
V_Cont <= std_logic_vector(unsigned(V_Cont) + 1);
else
V_Cont <= (others => '0');
end if;
end if;
(Ya, I used package numeric_std, sue me. Reformatted the heck out of your code to make it readable too).
And that gives you something that looks more reasonable (but hasn't really been checked, after all it's your design):
And the next Vertical event:
I also don't see any reason you couldn't use range constrained integers for H_Cont and V_Cont.
addendum
Because the answer caused confusion:
sorry, to be frank, i do understand what r u trying to achieve but i
dont understand your codes. – user317130 13 hours ago
I figured I'd redo the solution using the Synopsys version of std_logic packages. It makes the changes simpler and easier to see.
First, I created a CLOCK process that generated CLK_18 locally. This could have been in the test bench and simply driving CLK_18 with CLK_50, I didn't want any name confusion. I also commented out the I2C and PLL as being not supplied/uninvolved:
signal CLK_18: std_logic := '0'; -- default value to allow not in CLOCK process to run
Removing the unsupplied components:
-- component LCM_PLL2 port(inclk0: in std_logic;
-- c0: out std_logic);
-- end component;
--
-- component I2S_LCM_Config is port(iCLK: in std_logic;
-- iRST_N: in std_logic;
-- I2S_SCLK: out std_logic;
-- I2S_SDAT: inout std_logic;
-- I2S_SCEN: out std_logic);
-- end component;
And:
--
-- u0:LCM_PLL2 port map(inclk0=>CLOCK_50,
-- c0=>CLK_18);
-- u1:I2S_LCM_Config port map(iCLK=>CLOCK_50,
-- iRST_N=>KEY0,
-- I2S_SCLK=>LCM_SCLK,
-- I2S_SDAT=>I2S_SDAT,
-- I2S_SCEN=>LCM_SCEN);
And yes that leaves some signals undriven. We'll drive CLK_18 locally:
-- Dummy up CLK_18:
CLOCK:
process
begin
wait for 27.15 ns;
CLK_18 <= not CLK_18;
if Now > 80 ms then
wait;
end if;
end process;
-- Here instead of the test bench, could have jumpered CLOCK_50 to CLK_18
80 ms was rather excessive (a simulation will run until no events occur, everything is driven off the clock). Took a substantial length of time to simulate and the waveform dump was big (32 MB). It can be pared in half at least.
The change in operating the vertical counter is a bit more obvious using Synopsys standard logic libraries:
if H_Cont = H_SYNC_TOTAL - 1 then
if(V_Cont <(V_SYNC_TOTAL-1)) then
V_Cont <= V_Cont+'1';
else V_Cont <= "00000000000";
end if;
end if;
It consists of only operating the V_Cont counter in the last count of H_Cont so the counter only operates once per scan line.
The added test bench:
library ieee;
use ieee.std_logic_1164.all;
entity LCM_TB is
end entity;
architecture foo of LCM_TB is
signal CLOCK_50: std_logic := 'H';
signal KEY0: std_logic := '0';
signal SW: std_logic_vector(1 downto 0) := "11";
signal LCM_DATA: std_logic_vector(7 downto 0);
signal LCM_DCLK,
LCM_HSYNC,
LCM_VSYNC,
LCM_SCLK,
LCM_SDAT,
LCM_SCEN,
LCM_GRST,
LCM_SHDB: std_logic;
begin
-- CLOCK process found in DUT
DUT:
entity work.de2_lcm2
port map (
CLOCK_50 => CLOCK_50,
KEY0 => KEY0,
SW => SW,
LCM_DATA => LCM_DATA,
LCM_DCLK => LCM_DCLK,
LCM_HSYNC => LCM_HSYNC,
LCM_VSYNC => LCM_VSYNC,
LCM_SCLK => LCM_SCLK,
LCM_SDAT => LCM_SDAT,
LCM_SCEN => LCM_SCEN,
LCM_GRST => LCM_GRST,
LCM_SHDB => LCM_SHDB
);
STIMULUS: -- Just a reset
process
begin
wait for 100 ns;
KEY0 <= '1';
wait;
end process;
end architecture;
The build process:
ghdl -a --ieee=synopsys -fexplicit de2_lcm2.vhdl
ghdl -e --ieee=synopsys -fexplicit lcm_tb
ghdl -r lcm_tb --wave=lcm_tb.ghw
And because this is a Mac open *.ghw, or after establishing a save file open *.gtkw.
(OS X claims suffixes like Windows, the open command tells it to...).
And all this gives the same answer as the version using package numeric_std:
From the horizontal scroll bar in GTKWave you can see the simulation was twice as long as needed.
And instead of using std_logic_vector values you could have used unsigned with IEEE standard compliant package numeric_std with some slight modifications or converted H_Cont and V_Cont to range constrained integers (which will synthesize just fine), not forgetting to convert "00000000000" to 0 and ... + '1' to + 1 where appropriate.
I would have gotten back quicker but am in timezone GMT-12 (It's tomorrow here).
I write simple vhdl code for Uart receiver.
Simulation (iSIM) is fine but when implemented I have wrong reading behavior.
When synthetized ISE tell me there are latches on state machine end on data_fill(x).
have you any suggestion.
thanks in advance
gian
here the code
library ieee;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_UNSIGNED.all;
entity rx_uart is
port (
clk : in STD_LOGIC;
rst : in STD_LOGIC;
rx_data : in STD_LOGIC;
data_out: out STD_LOGIC_VECTOR(7 downto 0)
);
end rx_uart;
architecture fizzim of rx_uart is
-- state bits
subtype state_type is STD_LOGIC_VECTOR(2 downto 0);
constant idle: state_type:="000"; -- receive_en=0 load_en=0 cnt_en=0
constant receive: state_type:="101"; -- receive_en=1 load_en=0 cnt_en=1
constant stop_load: state_type:="010"; -- receive_en=0 load_en=1 cnt_en=0
signal state,nextstate: state_type;
signal cnt_en_internal: STD_LOGIC;
signal load_en_internal: STD_LOGIC;
signal receive_en_internal: STD_LOGIC;
signal count : integer range 0 to 54686:=0;
signal cnt_en : STD_LOGIC;
signal load_en : STD_LOGIC;
signal receive_en : STD_LOGIC;
signal data_fill : STD_LOGIC_VECTOR(9 downto 0);
-- comb always block
begin
COUNTER_EN : process(clk,rst,cnt_en) begin
if (rst ='1') then
count <= 0;
elsif rising_edge(clk) then
if (cnt_en ='1') then
count <= count+1;
else
count <= 0;
end if;
end if;
end process;
LOADER: process(clk,rst,load_en) begin
if (rst='1') then
data_out <= (others =>'0');
elsif (rising_edge(clk) and load_en='1')then
data_out <= data_fill(8 downto 1);
end if;
end process;
ASSIGNATION : process(clk,rst,receive_en) begin
if (rst ='1') then
data_fill <= (others =>'0');
elsif (receive_en='1') then
case count is
when 7812 =>
data_fill(1) <= rx_data;
when 13020 =>
data_fill(2) <= rx_data;
when 18228 =>
data_fill(3) <= rx_data;
when 23436 =>
data_fill(4) <= rx_data;
when 28664 =>
data_fill(5) <= rx_data;
when 33852 =>
data_fill(6) <= rx_data;
when 39060 =>
data_fill(7) <= rx_data;
when 44268 =>
data_fill(8) <= rx_data;
when 49476 =>
data_fill(9) <= rx_data;
when others =>
data_fill(0) <= '0';
end case;
end if;
end process;
COMB: process(state,clk,count,rst,rx_data) begin
case state is
when idle =>
if (rx_data='0') then
nextstate <= receive;
elsif (rx_data='1') then
nextstate <= idle;
end if;
when receive =>
if (count<=54685) then
nextstate <= receive;
elsif (count>54685) then
nextstate <= stop_load;
end if;
when stop_load =>
nextstate <= idle;
when others =>
end case;
end process;
-- Assign reg'd outputs to state bits
cnt_en_internal <= state(0);
load_en_internal <= state(1);
receive_en_internal <= state(2);
-- Port renames for vhdl
cnt_en <= cnt_en_internal;
load_en <= load_en_internal;
receive_en <= receive_en_internal;
-- sequential always block
FF: process(clk,rst,nextstate) begin
if (rst='1') then
state <= idle;
elsif (rising_edge(clk)) then
state <= nextstate;
end if;
end process;
end fizzim;
You need to understand when latches are generated. Latches are generated when you have an incomplete assignment in combinational logic. Your case statements are combinational. You do not completely assign all possibilities of your data signal and your state machine. When you have an incomplete assignment in a combinational process you will always generate a latch. Latches are bad!
Use your when others properly to assign all of your signals under all conditions. Your data_fill signal will always generate a latch because you don't handle all conditions for data 0:9 on all cases.
Read more about how to avoid latches in VHDL
Edit: It seems too that you don't consistently create sequential logic in VHDL. You need to be creating a clocked process or remove clk from your sensitivity list in a combinational process.
your design has several bad code sites besides your latch problem. I'll number them for better reference.
(1)
Xilinx XST won't recognize the implemented state machine as a FSM. See your XST report or the *.syr file. There won't be a FSM section. If XST finds no FSM it's not able to choose "the best" state encoding and optimize your FSM.
You should use an enum as a state-type and also initialize your state signal:
type t_state is (st_idle, st_receive, st_stop);
signal state : t_state := st_idle;
signal nextstate : t_state;
Your FSM process also needs default assignments (see Russell's explanation) like
nextstate <= state;
(2)
asynchronous resets are no good design practice and complicate timing closure calculations.
(3)
you are handling raw input signals from outside you FPGA without any timing information. to prevent meta stability problems place two D-flip-flops on the rx_data path. you should also add the async-reg and no-srl-extract attribute to these 2 registers to prevent XST optimizations
SIGNAL I_async : STD_LOGIC := '0';
SIGNAL I_sync : STD_LOGIC := '0';
-- Mark register "I_async" as asynchronous
ATTRIBUTE ASYNC_REG OF I_async : SIGNAL IS "TRUE";
-- Prevent XST from translating two FFs into SRL plus FF
ATTRIBUTE SHREG_EXTRACT OF I_async : SIGNAL IS "NO";
ATTRIBUTE SHREG_EXTRACT OF I_sync : SIGNAL IS "NO";
[...]
BEGIN
[...]
I_async <= In WHEN rising_edge(Clock);
I_sync <= I_async WHEN rising_edge(Clock);
Let 'In' be your asynchronous input. Now you can use I_sync in your clock domain. I choose to describe these 2 registers in a one-liner, you can also use a classic process :) The suffix '_async' allows you to define a matching timing rule in your *.xcf and *.ucf files.
I want to implement a random-number game on BASYS2. In this game there would be five LEDs chosen out of which one would turn on at random for a second or two (this time can be changed to increase or decrease the difficulty level of the game). Then the user is required to respond to this LED event by pressing the switch button behind it within the time that it is on. If he or she is able to do so successfully a point would be scored and it would be showed on the Seven Segment Display. If he or she fails no point would be scored. There would be 9 such events after which the game can be replayed.
Now following is my code (only for the random LED turning on). However, I am unable to fix it. Please somebody help. The FPGA I am using is BASYS2 SPARTAN 3E-100.
Thanks in advance to everyone.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_unsigned.ALL;
use IEEE.STD_LOGIC_arith.ALL;
entity random_number is
generic ( width : integer := 4 );
port (
clk : in std_logic;
reset : in std_logic;
random_num : out std_logic_vector (width-1 downto 0) --output vector
);
end random_number;
architecture Behavioral of random_number is
signal q: std_logic_vector(23 downto 0);
signal divided_clock: std_logic;
begin
process(clk, reset)
begin
if (reset = '1')then
q <= X"000000";
elsif(rising_edge(clk)) then
q <= q + 1;
end if;
end process;
divided_clock <= q(22);
process (divided_clock)
variable rand_temp : std_logic_vector(width-1 downto 0):=("1000");
variable temp : std_logic := '0';
begin
if(rising_edge(divided_clock)) then
temp := rand_temp(width-1) xor rand_temp(width-2);
rand_temp(width-1 downto 1) := rand_temp(width-2 downto 0);
rand_temp(0) := temp;
end if;
random_num <= rand_temp;
end process;
end Behavioral;
I think the second process should even run with the main clk and the devided clock should be an enable.
signal divided_enable: std_logic;
process(clk, reset)
begin
if (reset = '1')then
q <= X"000000";
elsif(rising_edge(clk)) then
q <= q + 1;
end if;
if (q(22) = '1') then
--short pulse wenn q bit 22 is high
divided_enable <= '1';
q <= (others => '0');
end if;
end process;
process (clk)
variable rand_temp : std_logic_vector(width-1 downto 0):=("1000");
variable temp : std_logic := '0';
begin
if(rising_edge(clk)) then
if(divided_enable = '1') then
temp := rand_temp(width-1) xor rand_temp(width-2);
rand_temp(width-1 downto 1) := rand_temp(width-2 downto 0);
rand_temp(0) := temp;
end if;
end if;
random_num <= rand_temp;
end process;
I don't know if this will fix all your problems. Please discribe compiler errors or errors in the behavior.
I have simple register and getting single bit values from 5 state machines (all at one time). These values are stored in a register as std_logic_vector and has to be given as an input to another module. Once the output of this register is being processed in another module, the index in the register where there was a change (e,g 0 to 1), the value at that index should reset (e,g 1 to 0) and it should take no further input for that particular index (but there is constant input coming from state machines). Any suggestion, how it should be done?
The register code is:
entity fault_reg is
port (
clk : in std_logic;
rst : in std_logic;
reg_in : in std_logic_vector(NUM_PORTS - 1 downto 0);
reg_out : out std_logic_vector(NUM_PORTS - 1 downto 0));
end fault_reg;
architecture Behavioral of fault_reg is
begin
reg_impl : process(clk, rst)
begin
if rst = '1' then
reg_out <= (others => '0');
elsif clk'event and clk='1' then
reg_out <= reg_in;
end if;
end process reg_impl;
end Behavioral;
I'm not entirely sure what you are asking, but it seems to me you want something like:
initialise your reg_out to all ones
then in the clocked process do a for loop to iterate over all the input bits and clear the bits which are set in the input
Like this:
reg_out <= reg_in;
for i in reg_in'range loop
if reg_in(i) = '1' then
masked_bits(i) := '1';
end if;
if masked_bits(i) = '1' then
reg_out(i) <= '0';
end if;
end loop;