This project is about adding user's custom peripheral core to MicroBlaze project on FPGA board "spartan 6 lx9". Using ISE Design Suite 14.6 and EDK.
My problem is being not enough experienced in writing VHDL code. I'm still getting 1-bit unintentional latches on signals: "data_bits" and "latest_value" from <0> til <15>, even though I have used recommended coding style for signal's assignment. I have set default values, but no success... Assignment of signal in each branch of case statement is not an option, since I want to retain value especially for "data_bits" since this vector is being build from several clock cycles. I'm trying to solve this problem for several days.
My questions are:
How I can fixed latches problem in this finite-state machine design? --Answered
I would like to get feedback on my state-machine design, styling etc. --Answered, but there is new code
Any design advice for staying on one state for several clocks cycles, using counters or there is a better technique? --Still expecting some advice
Initial Source Code:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity adc_16bit is
port(
clk : in std_logic;
rst : in std_logic;
data_reg_out : out std_logic_vector(31 downto 0);
control_reg : in std_logic_vector(31 downto 0);
SDO : in std_logic;
SCK : out std_logic;
CONV : out std_logic
);
end adc_16bit;
architecture Behavioral of adc_16bit is
type adc_states is (idle, conversation, clocking_low, clocking_high, receiving_bit, update_data);
signal State, NextState : adc_states;
signal data_bits : std_logic_vector(15 downto 0) := (others => '0');
signal latest_value : std_logic_vector(15 downto 0) := (others => '0');
signal conv_cnt : integer range 0 to 501 := 0;
signal clk_cnt : integer range 0 to 14 := 0;
signal bit_cnt : integer range 0 to 17 := 0;
begin
----------------------------------------------------------------------------------------
-- State Machine Register
----------------------------------------------------------------------------------------
StateReg:
process(clk, rst)
begin
if(clk'event and clk = '1') then
if(rst = '0') then
State <= idle;
else
State <= NextState;
end if;
end if;
end process StateReg;
----------------------------------------------------------------------------------------
-- Signals Register
----------------------------------------------------------------------------------------
TimerReg:
process(clk, rst)
begin
if(clk'event and clk = '1') then
--!default
conv_cnt <= conv_cnt;
clk_cnt <= clk_cnt;
bit_cnt <= bit_cnt;
--latest_value <= latest_value;
--data_bits <= data_bits;
case State is
when idle =>
conv_cnt <= 0;
clk_cnt <= 0;
bit_cnt <= 0;
when conversation =>
if(conv_cnt = 501) then
conv_cnt <= 0;
else
conv_cnt <= conv_cnt + 1;
end if;
when clocking_low =>
if(clk_cnt = 14) then
clk_cnt <= 0;
else
clk_cnt <= clk_cnt + 1;
end if;
when clocking_high =>
if(clk_cnt = 14) then
clk_cnt <= 0;
else
clk_cnt <= clk_cnt + 1;
end if;
when receiving_bit =>
if(bit_cnt = 16) then
bit_cnt <= 0;
else
bit_cnt <= bit_cnt + 1;
end if;
when update_data =>
end case;
end if;
end process TimerReg;
----------------------------------------------------------------------------------------
-- FSM Logic
----------------------------------------------------------------------------------------
FSM_Proc:
process(State, control_reg, conv_cnt, clk_cnt, bit_cnt )
begin
case State is
when idle =>
if(control_reg(0) = '1') then
NextState <= conversation;
else
NextState <= idle;
end if;
when conversation =>
if(conv_cnt = 500) then
NextState <= clocking_low;
else
NextState <= conversation;
end if;
when clocking_low =>
if(clk_cnt = 13) then
NextState <= clocking_high;
else
NextState <= clocking_low;
end if;
when clocking_high =>
if(clk_cnt = 13) then
NextState <= receiving_bit;
else
NextState <= clocking_high;
end if;
when receiving_bit =>
if(bit_cnt = 15) then
NextState <= update_data;
else
NextState <= clocking_low;
end if;
when update_data =>
if(control_reg(0) = '1') then
NextState <= conversation;
else
NextState <= idle;
end if;
end case;
end process FSM_Proc;
----------------------------------------------------------------------------------------
-- FSM Output
----------------------------------------------------------------------------------------
FSM_Output:
process(NextState, latest_value, data_bits, bit_cnt, SDO )
begin
--!default
CONV <= '0';
SCK <= '0';
data_reg_out(31 downto 16) <= (others => '0');
data_reg_out(15 downto 0) <= latest_value;
--data_bits <= data_bits;
--latest_value <= latest_value;
case NextState is
when idle =>
latest_value <= (others => '0');
data_bits <= (others => '0');
when conversation =>
CONV <= '1';
when clocking_low =>
SCK <= '0';
when clocking_high =>
SCK <= '1';
when receiving_bit =>
SCK <= '1';
--data_bits <= data_bits;
data_bits(bit_cnt) <= SDO;
when update_data =>
latest_value <= data_bits;
when others =>
--latest_value <= latest_value;
--data_bits <= data_bits;
end case;
end process FSM_Output;
end Behavioral;
EDIT
Thank you for all your responses! I decided to rewrite my FSM on single process and to add more information regarding my problem in order to make it more understandable for others who has similar problems!
Block Diagram of system:
http://i.stack.imgur.com/odCwR.png
Note: that right now I just want to simulate and verify stand alone adc_core itself without MicroBlaze and AXI interconnection block.
FSM Diagram:
http://i.stack.imgur.com/5qFdN.png
Single process source code:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity adc_chip_driver is
port(
clk : in std_logic;
rst : in std_logic;
data_reg_out : out std_logic_vector(31 downto 0);
control_reg : in std_logic_vector(31 downto 0);
SDO : in std_logic;
SCK : out std_logic;
CONV : out std_logic
);
end adc_chip_driver;
architecture Behavioral of adc_chip_driver is
type states is (idle, conversation, clocking_low, clocking_high, receiving_bit, update_data);
signal state : states;
signal data_bits : std_logic_vector(0 to 15) := (others => '0');
signal latest_value : std_logic_vector(15 downto 0) := (others => '0');
signal conv_cnt : integer range 0 to 500 := 0;
signal clk_cnt : integer range 0 to 13 := 0;
signal bit_cnt : integer range 0 to 15 := 0;
begin
process(clk, rst, control_reg)
begin
if(rst = '0') then
state <= idle;
data_bits <= (others => '0');
latest_value <= (others => '0');
data_reg_out <= (others => '0');
elsif(clk'event and clk = '1') then
--!Default Values
data_reg_out(31 downto 16) <= (others => '0'); --unused bits of register
data_reg_out(15 downto 0) <= latest_value; --data_reg_out is always tided to latast_value;
latest_value <= latest_value; --latest_value is being updated only once
data_bits <= data_bits; --has to retain value
conv_cnt <= conv_cnt;
clk_cnt <= clk_cnt;
bit_cnt <= bit_cnt;
case state is
when idle =>
--signals
conv_cnt <= 0;
clk_cnt <= 0;
bit_cnt <= 0;
--outputs
SCK <= '0';
CONV <= '0';
--logic
if(control_reg(0) = '1') then
state <= conversation;
else
state <= idle;
end if;
when conversation =>
--output
SCK <= '0';
CONV <= '1';
--logic
if(conv_cnt = 500) then
state <= clocking_low;
conv_cnt <= 0;
else
state <= conversation;
conv_cnt <= conv_cnt + 1;
end if;
when clocking_low =>
--ouput
SCK <= '0';
CONV <= '0';
--logic
if(clk_cnt = 13) then
clk_cnt <= 0;
state <= clocking_high;
else
clk_cnt <= clk_cnt + 1;
state <= clocking_low;
end if;
when clocking_high =>
--ouput
SCK <= '1';
CONV <= '0';
--logic
if(clk_cnt = 13) then
clk_cnt <= 0;
state <= receiving_bit;
else
clk_cnt <= clk_cnt + 1;
state <= clocking_high;
end if;
when receiving_bit =>
--signal
data_bits(bit_cnt) <= SDO;
--ouput
SCK <= '1';
CONV <= '0';
--logic
if(bit_cnt = 15) then
bit_cnt <= 0;
state <= update_data;
else
bit_cnt <= bit_cnt + 1;
state <= clocking_low;
end if;
when update_data =>
--signal
latest_value(15 downto 0) <= data_bits(0 to 15);
--ouput
SCK <= '0';
CONV <= '0';
--logic
if(control_reg(0) = '1') then
state <= conversation;
else
state <= idle;
end if;
end case;
end if;
end process;
end Behavioral;
Maybe I could receive some new feedback on single process design?
Also I still do you have unanswered question regarding usage of counters in specific FSM states. I have noticed that usually during second cycle on "clocking_low" and "clocking_high" counter actually starts at 1 instead of 0, I know that in this situation it's not a problem, but I can easily imagine where it could be important. I was thinking about after reset set counters to '-1', but maybe there is better solution?
Your code has a number of problems. To illustrate some of them, I tried to sketch your finite state machine in Figs. 1 and 2 below, based on the VHDL code that you provided.
First and most importantly, the design should begin with a top-level block diagram, showing the circuit ports (as in Fig. 1), followed by a detailed state transition diagram (as in Fig. 2 – incomplete here). Recall, for example, that the circuit outputs (data_reg_out, SCK, and CONV – Fig. 1) are the signals that the FSM is supposed to produce, so it is indispensable that these values be specified in all states (shown inside the state circles in Fig. 2). Once the diagram of Fig. 2 is fixed and completed, writing a corresponding VHDL code should be relatively straightforward (except for the timer - see comments below).
Other problems can be seen directly in the code. Some comments on the four processes follow.
The first process (StateReg), which stores the FSM state, is fine.
The second process (TimerReg) is also registered (under clk’event), which is necessary to build the timer. However, dealing with timers is one of the trickiest parts of any timed FSM, because you MUST devise a correct strategy for stopping/running the timer and also for zeroing it. For this, I suggest that you check reference 1 below, which deals with all possible kinds of FSM implementations from a hardware perspective, including an extensive study of timed FSMs.
The third process (FSM_Proc) defines the next state. It is not registered, which is as it should be. However, to check it, it is necessary to complete first the state transition diagram of Fig. 2.
The last process (FSM_Output) defines the machine outputs. It is not registered, which is as it should be in general. However, the list of outputs is not the same in all states, in spite of the default values. Note, for example, the existence of latest_value and data_bits in state idle, which do not appear in all states, thus causing the inference of latches. Additionally, this process is based on NextState instead of PresentState, which (besides being awkward) might reduce the circuit’s maximum speed.
I hope these comments motivate you to restart from the beginning.
1 V. A. Pedroni, Finite State Machines in Hardware: Theory and Design (with VHDL and SystemVerilog), MIT Press, Dec. 2013.
You get a latch if a signal is not assigned to on all possible paths, as it then becomes stateful.
To avoid the problem, make sure you always assign a value to the signal (one way is to assign a "default" value at the top of the process).
since I want to retain value especially for "data_bits" since this vector is being build from several clock cycles.
"Retaining a value" means state, not purely combinatorial logic. In which case, it should not be in your output process. It should be in your state-update process.
My solution to this has been to always use clocked processes for everything. There is no need to have a separate clocked process for the state register and a separate process for the state transitions. That's a style which was required years ago. In my opinion, you are better off putting everything into a single clocked process and then you cannot get latches.
If you must use two processes then get a VHDL 2008 compiler and use process(all) to ensure that all your signals are correctly in the sensitivity list, and then carefully ensure that every signal you assign to gets an assignment for every logical path through the process. The easiest way to achieve this is often to assign them all some 'default' values at the start of the process.
In a combinational process (like FSM_Output), you should never read a signal and write to the same signal. That is exactly what is going on here, for latest_value and data_bits.
Either create new signals latest_value_r and data_bits_r and copy the values in the clocked process, or stick to a single clocked process with no separate combinational process.
What hardware do you want for data_bits and latest_value? If you want to build a vector over several clock cycles, then you need a storage device. Your choices are: latch (level sensitive storage) and flip-flop (edge sensitive storage). If you don't want latches, then you must code flip-flops.
To code flip-flops use the "if clk='1' and clk'event then", just like you did in TimerReg process. You can alternatively use "if rising_edge(Clk) then" - I like this better for readablity, but the tools don't care either way.
I think where you went wrong is in your planning process. Code is just design capture. What is important is that you plan with a block diagram and know where your design requires flip-flops and where it requires combinational logic. Get this right and the rest is just applying coding templates. So make sure you understand this before you start coding.
It does not matter whether you code with only clocked processes or use a mix of clocked and combinational logic processes. I think the most important thing you do in your coding is make it readable. If you collect opinions, you will see they vary, #Martin and #Brian prefer a single clocked process. I prefer a 2 process statemachine - flip-flop and combinational (present state to next state and ouput decode). You used a 3 process statemachine - for me that is like drawing a bubble diagram to show state transitions and a separate one to show the output transitions. However at the end of the day, they all capture the same intent. As long it is clear to someone reading your code long after you have left, it should be ok.
Related
Please forgive myself if you will find some trivial errors in my code .. I'm still a beginner with VHDL.
Well, I have to deal with a serial interface from an ADC. The interface is quite simple ... there is a wire for the serial data (a frame of 24 bits), a signal DRDY that tells me when the new sample data is available and a serial clock (SCLK) that push the bit into (rising edge). Everything is running continuously...
I need to capture correctly the 24 bit of the sample, put them on a parallel bus (shift register) and provide a "data valid" signal for the blocks that will process the samples ...
Due to the fact that my system clock is x4 the frequency of the serial interface, i was thinking that doing the job with a FSM will be easy ...
When you look into the code you will see a process to capture the rising edges of the DRDY and SCLK.
Then a FSM with few states (Init, wait_drdy, wait_sclk, inc_count, check_count).
I use a counter (cnt unsigned) to check if I've already captured the 24 bits, using also to redirect the states of the FSM in "check_count" state.
Here a picture:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity serial_ads1675 is
Port (
clk : in STD_LOGIC;
reset : in STD_LOGIC;
sclk : in std_logic;
sdata : in std_logic;
drdy : in std_logic;
pdata : out std_logic_vector(23 downto 0);
pdready : out std_logic
);
end serial_ads1675;
architecture Behavioral of serial_ads1675 is
-- Internal declarations
signal ipdata : std_logic_vector (23 downto 0);
signal ipdready : std_logic;
signal tmp1, tmp2, tmp3, tmp4 : std_logic;
signal rise_drdy, rise_sclk : std_logic;
signal cnt : unsigned (4 downto 0);
type state is (init, wait_drdy, wait_sclk, inc_count, check_count);
signal actual_state, next_state : state;
begin
-- Concurrent statements
pdata <= ipdata;
pdready <= ipdready;
rise_drdy <= '1' when ((tmp1 = '1') and (tmp2 = '0')) else '0';
rise_sclk <= '1' when ((tmp3 = '1') and (tmp4 = '0')) else '0';
-- Process
process (clk, reset)
begin
if(reset = '0') then
tmp1 <= '0';
tmp2 <= '0';
tmp3 <= '0';
tmp4 <= '0';
elsif (falling_edge(clk)) then
tmp1 <= drdy;
tmp2 <= tmp1;
tmp3 <= sclk;
tmp4 <= tmp3;
end if;
end process;
process (reset, clk)
begin
if (reset = '0') then
actual_state <= init;
elsif (rising_edge(clk)) then
actual_state <= next_state;
end if;
end process;
process (rise_sclk, rise_drdy) -- Next State affectation
begin
case actual_state is
when init =>
next_state <= wait_drdy;
ipdata <= (others => '0');
ipdready <= '0';
cnt <= (others => '0');
when wait_drdy =>
if (rise_drdy = '0') then
next_state <= actual_state;
else
next_state <= wait_sclk;
end if;
cnt <= (others => '0');
when wait_sclk =>
if (rise_sclk = '0') then
next_state <= actual_state;
else
next_state <= inc_count;
end if;
ipdready <= '0';
when inc_count =>
next_state <= check_count;
cnt <= cnt + 1;
ipdready <= '0';
ipdata(23 downto 1) <= ipdata(22 downto 0);
ipdata(0) <= sdata;
when check_count =>
case cnt is
when "11000" =>
next_state <= wait_drdy;
ipdready <= '1';
when others =>
next_state <= wait_sclk;
ipdready <= '0';
end case;
when others =>
next_state <= init;
end case;
end process;
end Behavioral;
My problem is during the check_count state ...
I'm expecting that this state should last one system clock cycle, but actually it last much more.
Here a snapshot of the behavioral simulation:
Due to the fact that this state last more than expected, i miss the following SCLK pulse and don't record the next bit ...
I don't understand why this state last so many system clock cycles instead of just one ...
Anyone has some clues and bring some light in my dark night ?
Thanks in advance.
Edit: I've tried to change the signal cnt for an integer variable internal to the process of the FSM ... Same results
The error is this:
process (rise_sclk, rise_drdy) -- Next State affectation
begin
-- code omitted, but does generally this:
next_state <= SOME_VALUE;
end process;
Because the sensitivity list includes only the signals rise_sclk and rise_drdy, the process is "executed" only if any of these signals changes. You can follow this in the wave diagram.
You don't have a synchronous design running on clk. Put clk on the sensitivity list and base the decisions on the levels of rise_sclk and rise_drdy. As an excerpt:
process (clk) -- Next State affectation
begin
if rising_edge(clk) then
case actual_state is
when init =>
next_state <= wait_drdy;
-- and so on
end case;
end if;
end process;
I wrote a VHDL State Machine. It works some time until it gets stuck on an impossible state.
There are 7 states in total, but only two of them interesting for us. The state graph is simple:
IDLE2 <=> IDLE <=> Other states
In words, it means that initial state is IDLE. All other states can be reached through IDLE state. State IDLE2 can only jump to the IDLE state.
Fragment of the code:
ENTITY MC_PLD_vhd IS
PORT
(
--INPUTS
MC_WR: IN STD_LOGIC;
MC_RD: IN STD_LOGIC;
Tstart: IN STD_LOGIC;
CLK: IN STD_LOGIC;
--OUTPUTS
PLD_WR: OUT STD_LOGIC;
PLD_RD: OUT STD_LOGIC;
STS: OUT STD_LOGIC_VECTOR(7 DOWNTO 0)
);
END MC_PLD_vhd;
ARCHITECTURE MC_PLD_vhd_dff OF MC_PLD_vhd IS
TYPE tSTATE is
(
IDLE --0
,REC1 --1
,SEND1 --2
--I omit some not significant states
,IDLE2 --6
);
SIGNAL STATE :tSTATE := IDLE;
BEGIN
PROCESS(CLK) IS
BEGIN
IF RISING_EDGE(CLK) THEN
STS <= std_logic_vector(to_unsigned(tSTATE'pos(STATE), 8));
CASE STATE IS
WHEN IDLE =>
IF Tstart = '1' THEN
PLD_WR <= '1';
PLD_RD <= '0';
STATE <= SEND1; -- <!> stuck
ELSE
IF MC_WR = '1' THEN
PLD_RD <= '1';
PLD_WR <= '0';
STATE <= REC1;
ELSE
PLD_WR <= '0';
PLD_RD <= '0';
STATE <= IDLE2;
END IF;
END IF;
WHEN IDLE2 =>
PLD_WR <= '0';
PLD_RD <= '0';
STATE <= IDLE;
WHEN IsReceiving_wait_RD_rise_1 => <...>
-- Other left away, as they don't add any information to my question.
As you may notice, the IDLE state is not 'stable'. Every cycle the machine must leave the IDLE state, even if nothing is happening: IDLE jumps to IDLE2, and from IDLE2 it returns to IDLE the next clock cycle. And it works pretty well!
Also you may notice that I output STATE through STS independently from state. In Altera SignalTap I see normal operation: states jumps back and forth, and enters to other states at appropriate input events.
PROBLEM
Suddenly after many cycles of normal operation I see (in SignalTap) that STS = 0 and doesn't change in time. This means that STATE = IDLE, and the machine does NOT jump to IDLE2 (STS = 6). And it does NOT jump to any state by any input event, it's just stuck in state IDLE. Judging by outputs (PLD_WR = 1 and PLD_RD = 0) the code is stuck near the comment <!> stuck in the code above, but STATE <= SEND1 doesn't occur.
How can this be possible??
Disclaimer: The state IDLE2 and the output STS were implemented for debugging this particular problem.
Surprisingly, I managed to solve my own problem just after I posted this question, despite the fact that I struggled with it a several days!
ANSWER: I change switch-enum block to if-integer.
PROCESS(CLK) IS
CONSTANT STATE_IDLE :INTEGER := 0;
CONSTANT STATE_RECEIVE :INTEGER := 1;
--I omit other states
CONSTANT STATE_IDLE2 :INTEGER := 6;
VARIABLE STATE :INTEGER := 0;
BEGIN
IF RISING_EDGE(CLK) THEN
STS <= std_logic_vector(to_unsigned(STATE, 8));
IF STATE = STATE_IDLE THEN
IF Tstart = '1' THEN
PLD_WR <= '1';
PLD_RD <= '0';
STATE := STATE_SEND_wait_RD;
ELSE
IF MC_WR = '1' THEN
PLD_RD <= '1';
PLD_WR <= '0';
STATE := STATE_RECEIVE;
ELSE
PLD_WR <= '0';
PLD_RD <= '0';
STATE := STATE_IDLE2;
END IF;
END IF;
ELSIF STATE = STATE_IDLE2 THEN
PLD_WR <= '0';
PLD_RD <= '0';
STATE := STATE_IDLE;
ELSIF STATE = STATE_RECEIVE THEN
--Unsignificant states omitted
Further, I cut out debugging STS output and IDLE2 state because they are no longer needed. And it still works, fortunately!
But I'm still wondering why switch-enum does not properly work.
May be it's compiler fault, I use "good old" Quartus II 8.1 Web
I am developing a state machine in VHDL and it doesn't' seem to be functioning properly. The design is shown below:
SHARED VARIABLE XM_INDEX : NATURAL RANGE 0 TO 99 := 0;
SIGNAL XM_STATE_INDICATOR : STD_LOGIC_VECTOR (7 DOWNTO 0) := "00000000";
TYPE XM_STATE_TYPE IS (EMPTY, IDLE, POWER_UP, POWER_UP_CONFIRM,
CHANNEL_SELECT, CHANNEL_SELECT_CONFIRM, VOLUME_CHANGE,
VOLUME_CHANGE_CONFIRM, TRANSMIT_CHAR, TRANSMIT_CHAR_CONFIRM,
COMPLETED);
SIGNAL XM_CURRENT_STATE : XM_STATE_TYPE := EMPTY;
SIGNAL XM_NEXT_STATE : XM_STATE_TYPE := EMPTY;
XMStateMachineClock: PROCESS (CLK25, SYS_RST) IS
BEGIN
IF (SYS_RST = '1') THEN
XM_CURRENT_STATE <= EMPTY;
ELSIF (RISING_EDGE(CLK25)) THEN
XM_CURRENT_STATE <= XM_NEXT_STATE;
END IF;
END PROCESS XMStateMachineClock;
XMStateMachine: PROCESS (XM_CURRENT_STATE) IS
BEGIN
-- Pend on current XM state
CASE XM_CURRENT_STATE IS
-- Empty: Debug only
WHEN EMPTY =>
XM_NEXT_STATE <= IDLE;
XM_STATE_INDICATOR <= "00000001";
-- Idle: Idle state
WHEN IDLE =>
IF XM_POWER_UP = '1' THEN
XM_INDEX := 0;
XM_NEXT_STATE <= POWER_UP;
XM_STATE_INDICATOR <= "00000010";
ELSE
-- Remain in idle
XM_NEXT_STATE <= IDLE;
XM_STATE_INDICATOR <= "00000001";
END IF;
WHEN POWER_UP =>
XM_NEXT_STATE <= TRANSMIT_CHAR;
XM_STATE_INDICATOR <= "00000100";
WHEN TRANSMIT_CHAR =>
IF (XM_INDEX < 11) THEN
XM_NEXT_STATE <= TRANSMIT_CHAR_CONFIRM;
XM_STATE_INDICATOR <= "00001000";
ELSE
XM_NEXT_STATE <= COMPLETED;
XM_STATE_INDICATOR <= "00000000";
END IF;
WHEN TRANSMIT_CHAR_CONFIRM =>
XM_INDEX := XM_INDEX + 1;
XM_NEXT_STATE <= TRANSMIT_CHAR;
XM_STATE_INDICATOR <= "00000100";
WHEN COMPLETED =>
XM_NEXT_STATE <= COMPLETED;
XM_STATE_INDICATOR <= "00000000";
-- Default
WHEN OTHERS =>
END CASE;
END PROCESS XMStateMachine;
The state machine is being clocked at 25 MHz. Per my understanding, my state machine should progress between the states as follows:
However, what I see when I hook up my logic analyzer is the following:
It seems as if the state machine is only alternating between the transmit and transmit confirm states once, as opposed to the 11 times that is should, and I cannot figure out why.
If you make XM_INDEX a signal have an XM_INDEX_NEXT that is latched in your XMStateMachineClock process and then change XM_INDEX := XM_INDEX + 1 to XM_INDEX_NEXT <= XM_INDEX + 1. I believe that this will fix your issue. XMStateMachine will also need to be sensitive to XM_INDEX.
The example code isn't compete and there's some chance chaning xm_index from a shared variable might upset some plans for it's use, should more than one process write to it. You could note that the user is responsible for controlling exclusive access in -1993 shared variables.
Creating a MCVE by providing a complete entity and architecture pair:
library ieee;
use ieee.std_logic_1164.all;
entity xm_sm is
port (
clk25: in std_logic;
sys_rst: in std_logic;
xm_power_up: in std_logic
);
end entity;
architecture foo of xm_sm is
-- shared variable xm_index: natural range 0 to 99 := 0;
signal xm_index: natural range 0 to 99 := 0; -- CHANGED to SIGNAL
signal xm_index_nxt: natural range 0 to 99; -- ADDED
signal xm_state_indicator: std_logic_vector (7 downto 0) := "00000000";
type xm_state_type is (EMPTY, IDLE, POWER_UP, POWER_UP_CONFIRM,
CHANNEL_SELECT, CHANNEL_SELECT_CONFIRM,
VOLUME_CHANGE, VOLUME_CHANGE_CONFIRM,
TRANSMIT_CHAR, TRANSMIT_CHAR_CONFIRM,
COMPLETED);
signal xm_current_state: xm_state_type := EMPTY;
signal xm_next_state: xm_state_type := EMPTY;
begin
xmstatemachineclock:
process (clk25, sys_rst) is
begin
if sys_rst = '1' then
xm_current_state <= EMPTY;
xm_index <= 0; -- ADDED
elsif rising_edge(clk25) then
xm_current_state <= xm_next_state;
xm_index <= xm_index_nxt; -- ADDED
end if;
end process xmstatemachineclock;
xmstatemachine:
process (xm_current_state, xm_power_up) is
begin
-- pend on current xm state
case xm_current_state is
-- empty: debug only
when EMPTY =>
xm_next_state <= IDLE;
xm_state_indicator <= "00000001";
-- idle: idle state
when IDLE =>
if xm_power_up = '1' then
xm_index_nxt <= 0;
xm_next_state <= POWER_UP;
xm_state_indicator <= "00000010";
else
-- remain in idle
xm_next_state <= IDLE;
xm_state_indicator <= "00000001";
end if;
when POWER_UP =>
xm_next_state <= TRANSMIT_CHAR;
xm_state_indicator <= "00000100";
when TRANSMIT_CHAR =>
if xm_index < 11 then
xm_next_state <= TRANSMIT_CHAR_CONFIRM;
xm_state_indicator <= "00001000";
else
xm_next_state <= COMPLETED;
xm_state_indicator <= "00000000";
end if;
when TRANSMIT_CHAR_CONFIRM =>
if xm_index = 99 then -- protect again overflow -- ADDED
xm_index_nxt <= 0;
else
xm_index_nxt <= xm_index + 1; -- CHANGED
end if;
-- xm_index_nxt <= xm_index + 1;
xm_next_state <= TRANSMIT_CHAR;
xm_state_indicator <= "00000100";
when COMPLETED =>
xm_next_state <= COMPLETED;
xm_state_indicator <= "00000000";
-- default
when others =>
end case;
end process xmstatemachine;
end architecture;
This changes xm_index to a signal and including a next value as suggested by Alden in his answer. This works as long as there's only one process that writes to it. xm_index is also now set to 0 during reset. Additionally in the TRANSMIT_CHAR_CONFIRM of the xm_currrent_state case statement xm_index is protected against overflow as a matter of course. The range of xm_index (0 to 99) can be limited to the maximum value (11). It raises suspicions that we're not seeing all of the design.
Adding a test bench:
library ieee;
use ieee.std_logic_1164.all;
entity xm_sm_tb is
end entity;
architecture foo of xm_sm_tb is
signal clk25: std_logic := '0';
signal sys_rst: std_logic := '0';
signal xm_power_up: std_logic := '0';
begin
DUT:
entity work.xm_sm
port map (
clk25 => clk25,
sys_rst => sys_rst,
xm_power_up => xm_power_up
);
CLOCK:
process
begin
wait for 50 ns;
clk25 <= not clk25;
if now > 3.1 us then
wait;
end if;
end process;
STIMULI:
process
begin
wait for 100 ns;
sys_rst <= '1';
wait for 100 ns;
sys_rst <= '0';
wait for 200 ns;
xm_power_up <= '1';
wait for 100 ns;
xm_power_up <= '0';
wait;
end process;
end architecture;
and we get:
Where we see we go through all the index values before finishing.
The original code successfully simulated but appears to have not synthesized to a working design due to the combinatorical loop:
XM_INDEX := XM_INDEX + 1;
where xm_loop is latched by a presumably one hot state representation for state TRANSMIT_CHAR_CONFIRM as a latch enable.
In simulation the sensitivity list being devoid of xm_index would prevent the adder from ripple incrementing xm_index. If xm_index had been in the process sensitivity list it would caused a bounds check violation on assignment after reaching 100. (Integer types aren't modular, they don't wrap and aren't proofed against overflow).
In synthesis without seeing the console output we might presume that the ripply time is sufficient to push the value of xm_index above 11 reliably in one clock time without wrapping to less than 11.
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 have an FSM and it works. The synthesizer, however, complains that there are latches for "acc_x", "acc_y", and "data_out" and I understand why and why it is bad. I have no idea, however, how to rewrite the FSM so the state-part goes to the clocked process. Any ideas where to start from? Here is the code of the FSM:
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity storage is
port
(
clk_in : in std_logic;
reset : in std_logic;
element_in : in std_logic;
data_in : in signed(11 downto 0);
addr : in unsigned(9 downto 0);
add : in std_logic; -- add = '1' means add to RAM
-- add = '0' means write to RAM
dump : in std_logic;
element_out : out std_logic;
data_out : out signed(31 downto 0)
);
end storage;
architecture rtl of storage is
component bram is
port
(
clk : in std_logic;
we : in std_logic;
en : in std_logic;
addr : in unsigned(9 downto 0);
di : in signed(31 downto 0);
do : out signed(31 downto 0)
);
end component bram;
type state is (st_startwait, st_add, st_write);
signal current_state : state := st_startwait;
signal next_state : state := st_startwait;
signal we : std_logic;
signal en : std_logic;
signal di : signed(31 downto 0);
signal do : signed(31 downto 0);
signal acc_x : signed(31 downto 0);
signal acc_y : signed(31 downto 0);
begin
ram : bram port map
(
clk => clk_in,
we => we,
en => en,
addr => addr,
di => di,
do => do
);
process(clk_in)
begin
if rising_edge(clk_in) then
if (reset = '1') then
current_state <= st_startwait;
else
current_state <= next_state;
end if;
end if;
end process;
process(current_state, element_in, add, dump, data_in, do, acc_x, acc_y)
begin
element_out <= '0';
en <= '1';
we <= '0';
di <= (others => '0');
case current_state is
when st_startwait =>
if (element_in = '1') then
acc_x <= resize(data_in, acc_x'length);
next_state <= st_add;
else
next_state <= st_startwait;
end if;
when st_add =>
if (add = '1') then
acc_y <= acc_x + do;
else
acc_y <= acc_x;
end if;
next_state <= st_write;
when st_write =>
if (dump = '1') then
data_out <= acc_y;
element_out <= '1';
else
di <= acc_y;
we <= '1';
end if;
next_state <= st_startwait;
end case;
end process;
end rtl;
This is personal preference, but I think most people on here will agree with me on this one... do not use two processes to control your state machine. The whole previous_state next_state thing is total garbage in my opinion. It's really confusing and it tends to make latches - SURPRISE - You found that out. Try rewriting your state machine with a single clocked process and only one state machine signal.
Here's my attempt at rewriting your state machine. Note that I'm not sure the functionality that I have below will work for you. Simulate it to make sure it behaves the way you expect. For example the signal en is always tied to '1', not sure if you want that...
process (clk_in)
begin
if rising_edge(clk_in) then
element_out <= '0';
en <= '1'; -- this is set to 1 always?
we <= '0';
di <= (others => '0');
case state is
when st_startwait =>
if (element_in = '1') then
acc_x <= resize(data_in, acc_x'length);
state <= st_add;
end if;
when st_add =>
if (add = '1') then
acc_y <= acc_x + do;
else
acc_y <= acc_x;
end if;
state <= st_write;
when st_write =>
if (dump = '1') then
data_out <= acc_y;
element_out <= '1';
else
di <= acc_y;
we <= '1';
end if;
state <= st_startwait;
end case;
end if;
end process;
The reason for the inferred latches is that the case in the last process does
not drive all signals in all possible combinations of the sensitive signals.
So the process can finish without altering some of the output data for some of
the signal values to the process. To hold output in this way is the operation
of a latch, so latches are therefore inferred by the synthesis tool.
The latches applies only to acc_x, acc_y, and data_out, since all other
signals are assigned a default value in the beginning of the process.
You can fix this by either driving a default value for the last 3 signals in
the beginning of the process, for example 'X' for all bit to allow synthesis
freedom:
data_out <= (others => 'X');
acc_x <= (others => 'X');
acc_y <= (others => 'X');
Alternatively can you can ensure that all outputs are driven in all branches of
the case, and you should then also add a when others => branch to the case.
I suggest that you use the assign of default value to all signals, since this
is easier to write and maintain, instead of having to keep track of assign to
all driven signals in all branches of the case.
Clearly you need supplemental (clocked) registers (D flip flops).
Your need to ask yourself "what occurs to acc_x if the FSM is in (let say) state st_add ?". Your answer is "I dont want to modify acc_x in this state". So : write it explicitely, using clocked registers (such as the one used for state ; you can augment the clocked process with these supplemental registers). Do that Everywhere. That is the rule. Otherwise, synthesizers will infer transparent latches to memorize the previous value of acc_x : but these transparent latches violate the synchronous design principles. They structurally imply combinatorial loops in your designs, which are bad.
Put another way : ask yourself what is combinatorial and where are the registers ? If you have registers in mind, code them explicitly. Do not make combinatorial signals assigned and read in the same process.