I'm learning VHDL language right now and I have some problems of understanding a part of the code in my course. I don't understand in process freq_counter this statement -> if(pwm_count < max_pwm_count), because we don't know the value of max_pwm_count and also I don't see any incrementation of variable pwm_count.
Thank you, guys!
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.numeric_std.all;
entity PWM is
generic (
freq : integer := 50; --50Hz
INPUT_CLK : integer := 50000000; --50MHz
BITH_DEPTH : integer := 8
);
Port (
ENABLE : in std_logic;
CLK : in std_logic;
PWM_OUT : out std_logic;
DUTY_CYCLE : in std_logic_vector(BITH_DEPTH-1 downto 0)
);
end PWM;
architecture behavioral of PWM is
constant max_freq_count : integer:= INPUT_CLK/freq;
constant pwm_step : integer := max_freq_count/2**BITH_DEPTH;
signal PWM_value : std_logic := '0';
signal freq_count : integer range from 0 to max_freq_count := 0;
signal pwm_count : integer range from 0 to 2**BITH_DEPTH := 0;
signal max_pwm_count : integer range from 0 to 2**BITH_DEPTH := 0;
signal pwm_step_count : integer range from 0 to max_freq_count := 0;
begin
max_pwm_count <= TO_INTEGER(unsigned(DUTY_CYCLE));
PWM_OUT <= PWM_value;
freq_counter: process(CLK)
begin
if rising_edge(CLK) then
if(ENABLE='0') then
if(freq_count < max_freq_count) then
freq_count <= freq_count + 1;
if(pwm_count < max_pwm_count) then
PWM_value<='1';
if(pwm_step_count<pwm_step) then
pwm_step_count<=pwm_step_count+1;
else
pwm_step_count<=0;
pwm_count<=0;
end if;
else
pwm_value<='0';
end if;
else
freq_count <= 0;
pwm_count <= 0;
end if;
else
PWM_value <= '0';
end if;
end if;
end process freq_counter;
end PWM;
We DO know the value of max_pwm_count : it is initialised to 0 and never re-assigned. Therefore the IF can never be true and ... so on.
As far as incrementing PWM_Count is concerned, your understanding seems to be better than the author's, which puts you in a reasonable position for the necessary re-write.
I recommend first writing a testbench so you can observe its behaviour, and get it correct in simulation.
Related
I had written a small VHD file for simulating the behavior of a quadrature decoder, enclosed below. Simulating the design with a generic testbench works as expected. But after generating a synthesizable design with Quartus, I run into one of two problems (while playing with using unsigned, for example)
1. The position and direction signal are always at a constant 0 value throughout the post-synthesis simulation.
2. The position value seems to jump 10 values every 3-4 clock cycles, which I attribute to some jitter in data.
Does anyone have any recommendations to solve this issue? Is this mainly a timing problem or is there a major flaw in my design?
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.NUMERIC_STD.ALL;
entity quad_decoder is
port(rst : in std_logic;
clk : in std_logic;
a : in std_logic;
b : in std_logic;
direction : out std_logic;
position : out std_logic_vector(8 DOWNTO 0));
end quad_decoder;
architecture behavioral of quad_decoder is
begin
process(clk)
variable counter : integer range 0 to 360 := 0;
variable chanA,chanB : std_logic;
variable int_direction : std_logic;
begin
if (rst = '0') then
int_direction := '0';
counter := 0;
elsif (rising_edge(clk)) then
chanA := a;
chanB := b;
if (chanA = '1') and (chanB = '0') then
if (counter = 360) then
counter := 0;
else
counter:= counter + 1;
end if;
int_direction := '1';
elsif (chanA = '0') and (chanB = '1') then
if (counter = 0) then
counter := 360;
else
counter := counter-1;
end if;
int_direction := '0';
else
counter := counter;
int_direction := int_direction;
end if;
position <= std_logic_vector(to_unsigned(counter,9));
direction <= int_direction;
end if;
end process;
end behavioral;
The expected pre-synthesis snap is here.
I've linked an example snap of the post-synthesis simulation here. As seen, no change to position nor direction in multiple clock cycles.
If anyone is inquisitive, doing assignments right at the clock edge as well as turning the reset signal high proved to introduce all kinds of timing issues, which passed the multi-corner timing analysis test, but failed other tests in Quartus that I had failed to notice.
I can go into more details if my answer is vague.
I am using a high speed clock (from an internal PLL) and attempting to divide it down to generate 2 clocks with varying duty and phase relationships. The code operates correctly if I single-step the input clock. As the frequency increases, the phase and duty of the secondary output (iDM_out in the code sample) gets corrupted. At some frequencies, the duty cycle of the secondary output will be correct. At other frequencies, the duty cycle might go to 90 or 199%. Ditto with the phase relationship (DMDelay). I've seem some articles about the need for clock buffers so I've trying a few types BUFF, CLKBUF and CLKINT on the output and it seems to make it worse. Does anybody have any ideas about what might be causing this condition?
use work.A208_pckgs.all;
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity DutyPhaseMod is
port (
rst : in std_logic;
GLA : in std_logic; -- GLA is 36x faster than target frequency
EXDuty : in std_logic_vector(4 downto 0); -- the number of GLA clock cycles for the EX duty cycle
DMDuty : in std_logic_vector(4 downto 0); -- the number of GLA clock cycles for the DM duty cycle
DMDelay : in std_logic_vector(4 downto 0); -- the number of GLA clock cycles for the DM phase delay
EX_out : out std_logic;
DM_out : out std_logic
);
end entity;
architecture behavioral of DutyPhaseMod is
signal iGLA : std_logic;
signal iFREQlen : integer range 0 to 35;
begin
process (rst, GLA)
variable iEX_out : std_logic;
variable iDM_out : std_logic;
variable iEXctr : natural range 0 to 35;
variable iDMctr : natural range 0 to 35;
begin
iFREQlen <= 35; -- number of clock cycles
if rst = '1' then -- reset the counters on reset signal
iEXctr := 0;
iDMctr := 0;
elsif rising_edge(GLA) then
if iEXctr <= unsigned(EXDuty) then -- first part of EX the duty cycle
iEX_out := '1';
iEXctr := iEXctr + 1;
elsif iEXctr < iFREQlen then -- second part of EX duty cycle
iEX_out := '0';
iEXctr := iEXctr + 1;
else -- set for the start of the next cycle
iEX_out := '1';
iEXctr := 0;
end if;
if iEXctr = unsigned(DMDelay) then -- reset for DM phase offset
iDMctr := 0;
elsif iDMctr <= unsigned(DMDuty) then -- first part of the DM duty cycle
iDM_out := '1';
iDMctr := iDMctr + 1;
elsif iDMctr <= iFREQlen - 1 then -- second part of the DM duty cycle
iDM_out := '0';
iDMctr := iDMctr + 1;
else -- set for the start of the next cycle
iDM_out := '1';
end if;
EX_out <= iEX_out;
DM_out <= iDM_out;
end if;
end process;
end behavioral;
I'm going to go out on a limb and answer my own question. Feel free to comment if you feel that my answer is not correct (or you can add/clarify further.) I decided to split my entity into two processes and it seems to work properly. The first process handles the base clock division on the rising_edge of the high speed clock. The second process handles the secondary (phase delayed) clock on the falling_edge of the high speed clock.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity DutyPhaseMod is
port (
rst : in std_logic;
GLA : in std_logic;
EXDuty : in std_logic_vector(3 downto 0);
DMDuty : in std_logic_vector(3 downto 0);
DMDelay : in std_logic_vector(4 downto 0);
EX_out : out std_logic;
DM_out : out std_logic
);
end entity;
architecture behavioral of DutyPhaseMod is
signal iGLA : std_logic;
signal iFREQlen : integer range 0 to 35;
signal iEXctr : natural range 0 to 35;
begin
process (rst, GLA)
variable iEX_out : std_logic;
begin
iFREQlen <= 35;
if rst = '1' then
iEXctr <= 0;
elsif rising_edge(GLA) then
if iEXctr <= unsigned(EXDuty) then
iEX_out := '1'; --
iEXctr <= iEXctr + 1;
elsif iEXctr < iFREQlen then
iEX_out := '0';
iEXctr <= iEXctr + 1;
else
iEX_out := '1';
iEXctr <= 0;
end if;
EX_out <= iEX_out;
end if;
end process;
process (rst, GLA)
variable iDM_out : std_logic;
variable iDMctr : natural range 0 to 30;
begin
case FW_FREQ is
when F_46 =>
iFREQlen <= 35;
when F_82 =>
iFREQlen <= 23;
end case;
if rst = '1' then
iDMctr := 0;
elsif falling_edge(GLA) then
if iEXctr = unsigned(DMDelay) then
iDMctr := 0;
elsif iDMctr <= unsigned(DMDuty) then
iDM_out := '1';
iDMctr := iDMctr + 1;
elsif iDMctr <= iFREQlen - 1 then
iDM_out := '0';
iDMctr := iDMctr + 1;
else
iDM_out := '1';
end if;
DM_out <= iDM_out;
end if;
end process;
end behavioral;
I'm attempting to create synthesizable VHDL (function or procedure) for an ASIC (it must be part of the ASIC) that will look for the first '1' in a standard_logic_vector and output which vector position that '1' was in. For example, I have an 8-bit slv of "10001000" (a '1' in position 3 and 7). If I use this slv, the output should be 4 (the output is 1 based).
The actual VHDL will be searching a large slv, up to 512 bits in length. I tried implementing a binary search function but I get synthesis errors that states "Could not synthesize non-constant range values. [CDFG-231] [elaborate]
The non-constant range values are in file '...' on line 61" I indicated in the code below where it complains. I'm not sure how to implement a binary search algorithm without having non-constant range values. How would I modify this code so it's synthesizable?
I have attempted to search for binary search algorithms for HDL for potential code to look at and for my error, but I didn't find anything.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.std_logic_misc.all;
entity bin_search is
generic (
constant NREGS : positive := 16 -- number of registers
);
port (
clk_i : in std_logic; -- clock
bin_i : in unsigned( NREGS-1 downto 0 ); -- input
en_i : in std_logic; -- input enable
addr_o : out natural range 0 to NREGS -- first binary location
);
end bin_search;
architecture rtl of bin_search is
function f_bin_search( input: unsigned; nob: positive ) return natural is
constant nbits : positive := 2**nob;
variable lower : natural range 0 to 1 := 0;
variable upper : natural range 0 to 1 := 0;
variable idx : natural range 0 to nob := 4;
variable cnt : natural range 0 to nbits := 0;
variable mid : positive range 1 to nbits := nbits/2; --
variable ll : natural range 0 to nbits := 0;
variable ul : positive range 1 to nbits := nbits; --
begin
if input = 0 then
cnt := 0;
return cnt;
else
loop1: while ( idx > 0 ) loop
if ( input( mid-1 downto ll ) > 0 ) then -- <===WHERE SYNTH COMPLAINS
lower := 1;
else
lower := 0;
end if;
if ( input( ul-1 downto mid ) > 0 ) then
upper := 1;
else
upper := 0;
end if;
if ( idx = 1 ) then
if ( lower = 1 ) then
cnt := mid;
else
cnt := ul;
end if;
elsif ( lower = 1 ) then
ul := mid;
mid := ( ( ll+ul )/2 );
elsif ( upper = 1 ) then
ll := mid;
mid := ( ll+ul )/2;
else
cnt := 0;
exit loop1;
end if;
idx := idx-1;
end loop loop1;
return cnt;
end if;
end f_bin_search;
begin
test_proc: process ( clk_i )
begin
if rising_edge( clk_i ) then
if en_i = '1' then
addr_o <= f_bin_search( bin_i, 4 );
end if;
end if;
end process test_proc;
end rtl;
Here's a simple test bench where the input is inc'd by '1'. The addr_o should be the location (1 based) of the input lsb with a '1'.
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_misc.all;
use ieee.numeric_std.all;
entity bin_search_tb is
end bin_search_tb;
architecture behavior of bin_search_tb is
constant NREGS : positive := 16;
signal clk : std_logic;
signal input : unsigned( NREGS-1 downto 0 );
signal start : std_logic;
signal addr : natural range 0 to NREGS;
constant clk_per : time := 1 ns;
signal row : natural range 0 to 2**NREGS-1;
begin
bin_search_inst: entity work.bin_search( rtl )
generic map (
NREGS => NREGS
)
port map (
clk_i => clk, -- master clock
bin_i => input, -- captured events
en_i => start, -- start binary search
addr_o => addr -- addr where the first '1' appears
);
-- master clock process
clk_proc: process
begin
clk <= '0';
wait for clk_per / 2;
clk <= '1';
wait for clk_per / 2;
end process clk_proc;
--
stim1_proc: process
begin
input <= ( others => '0' );
start <= '0';
row <= 1;
wait until clk'event and clk = '1';
loop
wait until clk'event and clk = '1';
input <= to_unsigned( row, input'length );
start <= '1';
wait until clk'event and clk = '1';
start <= '0';
wait for 4*clk_per;
row <= row+1;
end loop;
end process stim1_proc;
end architecture behavior;
Thanks for your assistance!
-Jason
Edited code and added a testbench
Your design will most certainly depend on latency and other performance requirements, but, you could use some combination of or-reduction, sequencers (for mux selection of sliced vectors), shift register, and counters. I drew up a simple circuit that should find your lsb instance of "1" in ~30 clock cycles
The RTL translation that implements this design should be straight forward.
You say that you are thinking in hardware, but in fact you're not. Or you are misleading yourself.
input( mid-1 downto ll ) > 0
is not an OR-reduction, but a comparison operation. You must know > is the larger than comparison operator. The synthesis will therefor infer a comparator. But how many inputs must that comparator have, I ask? Well, there's your problem: it depends on the value of mid, which:
initially depends on the value of nbits, which depends on the value of nob which is a variable input for the function.
is changed within the loop. Thus it's value is not constant.
A hardware component cannot have a variable amount of wires.
But why do you want binary search? Why not keep-it-simple?
library ieee;
use ieee.std_logic_1164.all;
entity detect_one is
generic(
input_size : positive := 512);
port(
input : in std_logic_vector (input_size-1 downto 0);
output : out natural range 0 to input_size);
end entity;
architecture rtl of detect_one is
begin
main: process(input)
begin
output <= 0;
for i in input_size-1 downto 0 loop
if input(i)='1' then
output <= i+1;
end if;
end loop;
end process;
end architecture;
entity detect_one_tb is end entity;
library ieee;
architecture behavior of detect_one_tb is
constant input_size : positive := 512;
use ieee.std_logic_1164.all;
signal input : std_logic_vector (input_size-1 downto 0) := (others => '0');
signal output : integer;
begin
DUT : entity work.detect_one
generic map ( input_size => input_size )
port map(
input => input,
output => output);
test: process begin
wait for 1 ns;
assert (output = 0) report "initial test failure" severity warning;
for i in 0 to input_size-1 loop
input <= (others => '0');
input(i) <= '1';
wait for 1 ns;
assert (output = i+1) report "single ones test failure" severity warning;
end loop;
input <= (others => '1');
wait for 1 ns;
assert (output = 1) report "initial multiple ones test failure" severity warning;
for i in 0 to input_size-2 loop
input(i) <= '0';
wait for 1 ns;
assert (output = i+2) report "multiple ones test failure" severity warning;
end loop;
wait;
end process;
end architecture;
I am learning VHDL right now and I tried to implement UART (1 start bit, 8 data bits, 1 stop bit) to periodically send a hardcoded string.
Everything works as expected - I receive string every 1 second. However, there is no second character.
No matter how long the string is, which character it is. I checked this fact on a oscilloscope and there is no waveform for this particular character. 1 start bit, 8 bits for first character, stop bit, start bit and 8 bits for third character, not the second one.
Following code is for 10 MHz clock divided to send with ~38 400 bits per second, I also tried with 9600 bits per second, both the same problem.
I'm using Altera MAX10 dev board: http://maximator-fpga.org/
Short video how it works:
https://gfycat.com/JoyousIlliterateGuillemot
UART.vhd:
LIBRARY ieee;
USE ieee.std_logic_1164.all;
use ieee.numeric_std.ALL;
use ieee.std_logic_arith.all;
entity UART is
port (
clk_10mhz: in STD_LOGIC;
txPin: out STD_LOGIC
);
end entity;
architecture Test of UART is
signal txStart: STD_LOGIC;
signal txIdle: STD_LOGIC;
signal txData: STD_LOGIC_VECTOR(7 downto 0);
component TX is
port (
clk_in: in STD_LOGIC;
start: in STD_LOGIC;
data: in STD_LOGIC_VECTOR(7 downto 0);
tx: out STD_LOGIC;
txIdle: out STD_LOGIC
);
end component TX;
begin
process (clk_10mhz, txIdle)
variable clkDividerCounter : integer range 0 to 10000000;
variable textToSend : string(1 to 31) := "Hello darkness my old friend!" & CR & LF;
variable currentCharacterIndex : integer range 0 to 31;
begin
if (rising_edge(clk_10mhz)) then
if (clkDividerCounter < 10000000) then
clkDividerCounter := clkDividerCounter + 1;
else
clkDividerCounter := 0;
currentCharacterIndex := 1;
end if;
if (txIdle = '1' and currentCharacterIndex > 0) then
txData <= CONV_STD_LOGIC_VECTOR(character'pos(textToSend(currentCharacterIndex)),8);
txStart <= '1';
if (currentCharacterIndex < 31) then
currentCharacterIndex := currentCharacterIndex + 1;
else
currentCharacterIndex := 0;
txStart <= '0';
end if;
end if;
end if;
end process;
u1: TX port map (clk_10mhz, txStart, txData, txPin, txIdle);
end Test;
TX.vhd:
LIBRARY ieee;
USE ieee.std_logic_1164.all;
use ieee.numeric_std.ALL;
entity TX is
port (
clk_in: in STD_LOGIC;
start: in STD_LOGIC;
data: in STD_LOGIC_VECTOR(7 downto 0);
tx: out STD_LOGIC;
txIdle: out STD_LOGIC
);
end entity;
architecture Test of TX is
signal idle: STD_LOGIC;
begin
process (clk_in)
variable bitIndex : integer range 0 to 9;
variable clkDividerCounter : integer range 0 to 260;
variable dataFrame : STD_LOGIC_VECTOR(9 downto 0);
variable dataFrameCurrentIndex : integer range 0 to 9;
begin
if (rising_edge(clk_in)) then
if (start = '1' and idle = '1') then
dataFrame(0) := '0';
dataFrame(8 downto 1) := data;
dataFrame(9) := '1';
dataFrameCurrentIndex := 0;
idle <= '0';
end if;
if (idle = '0') then
if (clkDividerCounter < 260) then
clkDividerCounter := clkDividerCounter + 1;
else
if (dataFrameCurrentIndex <= 9) then
tx <= dataFrame(dataFrameCurrentIndex);
dataFrameCurrentIndex := dataFrameCurrentIndex + 1;
else
idle <= '1';
end if;
clkDividerCounter := 0;
end if;
end if;
txIdle <= idle;
end if;
end process;
end Test;
Move the line
txIdle <= idle;
from TX.vhd outside the process. Signals take their new value after the process ends.
For example:
idle <= '0';
txIdle <= idle;
Will set txIdle to '1' if idle was '1' when the two statements were executed inside a process. You should notice that this means that txIdle will be '1' for two consecutive cycles and causes currentCharacterIndex to increment twice at the start.
Note that contrary to signals, variable take their new value when the assigning statement is encountered, and not at the end of the process as signals do.
While your code is not that terrible for a beginner, I recommend to use only signal when you start learning VHDL. It is much easier to make mistake with variables, or describe sub-optimal or broken implementation.
Also, as Brian mentioned, don't use std_logic_arith, especially when using numeric_std. They are conflicting with each other (some tools deal with it though) and std_logic_arith is not a IEEE standard, while numeric_std is.
Finally, simulation is a crucial part of hardware design. To avoid uninitialized pin, add a reset to your circuit, which is generally a good idea.
I am working on an IR Decoder in VHDL and I know that the widths of an IR 1 bit is 1.2 ms, an IR 0 bit is 0.6 ms, and the start bit is 2.5 ms. I am trying to make a counter that takes in the 50MHz clock and converts to tenths of a millisecond. How can I do this?
entity counter is
Port ( EN : in STD_LOGIC;
RESET : in STD_LOGIC;
CLK : in STD_LOGIC;
COUNT : out STD_LOGIC_VECTOR (4 downto 0));
end counter;
architecture Behavioral of counter is
constant max_count : integer := (2);
begin
startCounter: process(EN, RESET, CLK)
variable cnt : integer := 0;
variable div_cnt : integer := 0;
begin
if (RESET = '1') then
cnt := 0;
div_cnt := 0;
elsif (EN = '1' and rising_edge(CLK)) then
if (cnt = max_count) then
cnt := 0;
div_cnt:= div_cnt + 1;
else
cnt := cnt + 1;
end if;
end if;
COUNT <= conv_std_logic_vector(cnt, 5);
-- COUNT <= temp_count(16 downto 13);
end process startCounter;
end Behavioral;
Since you have a 50 MHz clock and want to generate a 0.1 msec pulse, you can use the ieee library, math_real, to compute the number of 50 MHz clocks to create a 0.1 msec pulse. Here's a code fragment.
library ieee;
use ieee.math_real.all;
-- omitting for clarity...
-- generate one clk cycle pulse with period of 0.1 msec
gen_0p1mspulse_p : process(Clk)
constant CLK_PERIOD : real := 1/50e6;
constant PULSE_PERIOD : real := 0.1e-3;
constant MAX_CNT : integer := INTEGER(PULSE_PERIOD/CLK_PERIOD);
variable cnt : integer range 0 to MAX_CNT-1 := 0;
begin
if rising_edge(Clk) then
if reset = '1' then
cnt := 0;
pulse_0p1msec <= '0';
else
pulse_0p1msec <= '0'; -- default value
if cnt < MAX_CNT-1 then
cnt := cnt + 1;
else
cnt := 0;
pulse_0p1msec <= '1';
end if;
end if;
end if;
end process;
-- logic using 0.1 msec pulse
your_logic_p : process(Clk)
begin
if rising_edge(Clk) then
if reset = '1' then
your_cnt := 0;
else
if pulse_0p1msec = '1' then
-- insert your logic here
end if;
end if;
end if;
end process;
I like to split up my VHDL processes so that they're short. I also prefer to use synchronous resets and enables since they synthesize to less hardware for Xilinx FPGAs as well as running at a higher clock rates. Hope that addresses your issue.