Finite State Machine working in simulation, but not working on actual FPGA board

Status
Not open for further replies.

goodpranoy

Junior Member level 3
Joined
Dec 5, 2013
Messages
29
Helped
0
Reputation
0
Reaction score
0
Trophy points
1
Visit site
Activity points
201
hi guys,

i've written the following code as a control unit for my project (FPGA implementation of a functional microcontroller).

the code is working in simulation using modelsim.

also it is synthesised using xilinx.

but when i program it on an actual FPGA(spatan 3) i'm not getting correct working.

the part which is not working is when i give the IN instruction.

for entering a data the in_s state will continue until the enter pin has a low to high transition. (refer lines 104 to 110 of program)

but it is not working.

the in_s state is continuing infinitely.

please help me with this.

Thanks in advance.

Code:
library ieee;
use ieee.std_logic_1164.all;

entity mealy is
port (clk : in std_logic;
      reset : in std_logic;
      enter : in std_logic;
      input:in std_logic_vector(3 downto 0);
      output:out std_logic_vector(3 downto 0);
      pc_enable: out std_logic;
      alu_enable: out std_logic;
      rom_enable: out std_logic;
      reg_enable: out std_logic;
      ir_enable: out std_logic;
      load_a:out std_logic_vector(2 downto 0);
      load_b:out std_logic_vector(2 downto 0);
      pc_reset:out std_logic;
      a_zero_status:in std_logic;
      sel: out std_logic
  );
end mealy;

architecture behavioral of mealy is

type state_type is (fetch,decode,add_s,sub_s,mov_ab_s,and_s,or_s,mov_ba_s,in_s,out_s,jmp_s,jnz_s,jz_s,inc_s,dec_s,halt_s,rab,wa,wb,ra,nop_s);  --type of state machine.
signal current_s,next_s: state_type;  --current and next state declaration.
signal enter_r:std_logic;

begin

process (clk,reset)
begin
 if (reset='1') then
  current_s <= fetch;  --default state on reset.
  pc_reset<='1';
elsif (rising_edge(clk)) then
  current_s <= next_s;   --state change.
  pc_reset<='0';
end if;
end process;

--state machine process.
process (current_s,enter,input,a_zero_status)
begin
  case current_s is
    
    when fetch =>        --when current state is "fetch"
    next_s <= decode;
       
    when decode =>       --when current state is "decode"
    if(input ="0000") then
    next_s <= rab;
    elsif(input ="0001") then
    next_s <= rab;
    elsif(input ="0010") then
    next_s <= rab;
    elsif(input ="0011") then
    next_s <= rab;
    elsif(input ="0100") then
    next_s <= rab;
    elsif(input ="0101") then
    next_s <= ra;
    elsif(input ="0110") then
    next_s <= in_s;
    elsif(input ="0111") then
    next_s <= ra;
    elsif(input ="1000") then
    next_s <= jmp_s;
    elsif(input ="1001") then
    next_s <= jnz_s;
    elsif(input ="1010") then
    next_s <= jz_s;
    elsif(input ="1011") then
    next_s <= nop_s;
    elsif(input ="1100") then
    next_s <= rab;
    elsif(input ="1101") then
    next_s <= rab;
    elsif(input ="1111") then
    next_s <= halt_s;
    
    else
    next_s<=decode;
    end if;


    when add_s =>         --when current state is "add_s"
    next_s <= wa;
    
    when sub_s =>         --when current state is "sub_s"
    next_s <= wa;
    
    when mov_ab_s =>         --when current state is "mov_ab_s"
    next_s <= wa;
    
    when and_s =>         --when current state is "and_s"
    next_s <= wa;
    
    when or_s =>         --when current state is "or_s"
    next_s <= wa;
    
    when mov_ba_s =>         --when current state is "mov_ba_s"
    next_s <= wb;
    
    when in_s =>         --when current state is "in_s"
    enter_r<=enter;
    if(enter_r='0' and enter='1') then
    next_s <= fetch;
    else
    next_s <= in_s;
    end if;
    
    when out_s =>         --when current state is "out_s"
    next_s <= fetch;
    
    when jmp_s =>         --when current state is "jmp_s"
    next_s <= fetch;
    
    when jnz_s =>         --when current state is "jnz_s"
    next_s <= fetch;
    
    when jz_s =>         --when current state is "jz_s"
    next_s <= fetch;
    
    when nop_s =>         --when current state is "nop_s"
    next_s <= fetch;
    
    when inc_s =>         --when current state is "inc_s"
    next_s <= wa;
    
    when dec_s =>         --when current state is "dec_s"
    next_s <= wa;
    
    when halt_s =>         --when current state is "halt_s"
    next_s <= halt_s;

    when rab =>       --when current state is "rab"
    if(input ="0000") then
    next_s <= add_s;
    elsif(input ="0001") then
    next_s <= sub_s;
    elsif(input ="0010") then
    next_s <= mov_ab_s;
    elsif(input ="0011") then
    next_s <= and_s;
    elsif(input ="0100") then
    next_s <= or_s;
    elsif(input ="1100") then
    next_s <= inc_s;
    elsif(input ="1101") then
    next_s <= dec_s;
    
    end if;
    
    when wa =>         --when current state is "wa"
    next_s <= fetch;
    
    when wb =>         --when current state is "wb"
    next_s <= fetch;
    
    when ra =>         --when current state is "wb"
    if(input ="0101") then
    next_s <= mov_ba_s;
    elsif(input ="0111") then
    next_s <= out_s;
    end if;
    
         
end case;
end process;
    
    output_logic:process(current_s)
    begin
        case current_s is
            
        when fetch=>
                pc_enable<='1';
                alu_enable<='0';
                rom_enable<='1';
                reg_enable<='0';
                ir_enable<='0';
                load_a<="UUU";
                load_b<="UUU";
                sel<='U';
                
        when decode=>
                pc_enable<='0';
                alu_enable<='0';
                rom_enable<='0';
                reg_enable<='0';
                ir_enable<='1';
                load_a<="UUU";
                load_b<="UUU";
                sel<='U';
                
          when add_s=>
                pc_enable<='0';
                alu_enable<='1';
                rom_enable<='0';
                reg_enable<='0';
                ir_enable<='0';
                load_a<="UUU";
                load_b<="UUU";
                sel<='U';
                output<="0000";
                
          when sub_s=>
                pc_enable<='0';
                alu_enable<='1';
                rom_enable<='0';
                reg_enable<='0';
                ir_enable<='0';
                load_a<="UUU";
                load_b<="UUU";
                sel<='U';
                output<="0001";
                
         when mov_ab_s=>
                pc_enable<='0';
                alu_enable<='1';
                rom_enable<='0';
                reg_enable<='0';
                ir_enable<='0';
                load_a<="110";
                load_b<="110";
                sel<='0';
                output<="0010";
                
        when and_s=>
                pc_enable<='0';
                alu_enable<='1';
                rom_enable<='0';
                reg_enable<='0';
                ir_enable<='0';
                load_a<="UUU";
                load_b<="UUU";
                sel<='U';
                output<="0011";
                
        when or_s=>
                pc_enable<='0';
                alu_enable<='1';
                rom_enable<='0';
                reg_enable<='0';
                ir_enable<='0';
                load_a<="UUU";
                load_b<="UUU";
                sel<='U';
                output<="0100";
                
        when mov_ba_s=>
                pc_enable<='0';
                alu_enable<='1';
                rom_enable<='0';
                reg_enable<='0';
                ir_enable<='0';
                load_a<="UUU";
                load_b<="UUU";
                sel<='U';
                output<="0101";
                
        when in_s=>
                pc_enable<='0';
                alu_enable<='0';
                rom_enable<='0';
                reg_enable<='1';
                ir_enable<='0';
                load_a<="110";
                load_b<="UUU";
                sel<='1'; 
                output<="0110";
                
        when out_s=>
                pc_enable<='0';
                alu_enable<='1';
                rom_enable<='0';
                reg_enable<='0';
                ir_enable<='0';
                load_a<="UUU";
                load_b<="UUU";
                sel<='0';
                output<="0111";
                
        when jmp_s=>
                pc_enable<='0';
                alu_enable<='0';
                rom_enable<='0';
                reg_enable<='0';
                ir_enable<='0';
                load_a<="UUU";
                load_b<="UUU";
                sel<='0';
                output<="1000";
                
        when jnz_s=>
                pc_enable<='0';
                alu_enable<='0';
                rom_enable<='0';
                reg_enable<='0';
                ir_enable<='0';
                load_a<="UUU";
                load_b<="UUU";
                sel<='0';
                output<="1001";
                
        when jz_s=>
                pc_enable<='0';
                alu_enable<='0';
                rom_enable<='0';
                reg_enable<='0';
                ir_enable<='0';
                load_a<="UUU";
                load_b<="UUU";
                sel<='0';
                output<="1010";
                
        when nop_s=>
                pc_enable<='0';
                alu_enable<='0';
                rom_enable<='0';
                reg_enable<='0';
                ir_enable<='0';
                load_a<="UUU";
                load_b<="UUU";
                sel<='0';
                output<="1011";
                
        when inc_s=>
                pc_enable<='0';
                alu_enable<='1';
                rom_enable<='0';
                reg_enable<='0';
                ir_enable<='0';
                load_a<="UUU";
                load_b<="UUU";
                sel<='U';
                output<="1100";
                
        when dec_s=>
                pc_enable<='0';
                alu_enable<='1';
                rom_enable<='0';
                reg_enable<='0';
                ir_enable<='0';
                load_a<="UUU";
                load_b<="UUU";
                sel<='U';
                output<="1101";
                
        when halt_s=>
                pc_enable<='0';
                alu_enable<='1';
                rom_enable<='0';
                reg_enable<='1';
                ir_enable<='0';
                load_a<="101";
                load_b<="UUU";
                sel<='U';
                output<="0111";
                
         when wa=>
                pc_enable<='0';
                alu_enable<='0';
                rom_enable<='0';
                reg_enable<='1';
                ir_enable<='0';
                load_a<="110";
                load_b<="UUU";
                sel<='0';
                
          when wb=>
                pc_enable<='0';
                alu_enable<='0';
                rom_enable<='0';
                reg_enable<='1';
                ir_enable<='0';
                load_a<="UUU";
                load_b<="110";
                sel<='U';
                
          when rab=>
                pc_enable<='0';
                alu_enable<='0';
                rom_enable<='0';
                reg_enable<='1';
                ir_enable<='0';
                load_a<="101";
                load_b<="101";
                sel<='0';
                
        when ra=>
                pc_enable<='0';
                alu_enable<='0';
                rom_enable<='0';
                reg_enable<='1';
                ir_enable<='0';
                load_a<="101";
                load_b<="UUU";
                sel<='U';
                                    
  end case;
end process;

end behavioral;
 
Last edited:

Im very surprised this compiled in XIlinx. You can only use a rising_edge() function on a clock - and then you cannot use it 3 times in a process. It will work in simulation because you can write any code you want for simulation, but if it doesnt follow the correct templates for synthesis it wont synthesis. So you MUST remove the rising_edge() function out of the async part of the state machine. You're going to have to do the detection manually, register the "enter" signal and then compare the registered version to the input.

Did you check the RTL diagram? did it mach the logic you expected? I expect it didnt, or you didnt even look at it?
 


sir,
first of all i am sorry, i posted another code .

i have edited my program and posted it in the previous post.

in this program i have only used rising edge detection once in each process.
 

The problem is you've created a latch with the enter_r signal - not a register. You need to put enter_r in a clocked process (you could just stick it in the current_s process). It can be assigned on every clock edge.
 
sir did you mean like this??

Code:
process (clk,reset)
begin
 if (reset='1') then
  current_s <= fetch;  --default state on reset.
  pc_reset<='1';
elsif (rising_edge(clk)) then
  current_s <= next_s;   --state change.
  
  enter_r<=enter;
  
  pc_reset<='0';
end if;
end process;
 

Yes, but make sure you reset it as well, otherwise you're emulating a sync enable with the reset. If you dont want an async reset on it, put it in it's own process.
 
Yes, but make sure you reset it as well, otherwise you're emulating a sync enable with the reset.

sir,

i am new to VHDL

so i didn't understand your reply completely.

could you please explain it with a code?
 

one example would be:
Code:
process (clk, rst) is
begin
  if rising_edge(clk) then
    enter_r <= enter;
    state <= next_state;
  end if;
  if rst = '1' then
    state <= fetch; -- this is also bad practice, see more later
    --enter_r <= enter; -- this is optional as enter_r is not used until several cycles after reset.
  end if;

The state machine should not have an async reset unless you ensure it is deasserted synchronously. This is because the reset signal may not reach all bits of the state registers at exactly the same time. In your case, the first cycle after reset is to go to "decode". if "fetch" is state "0001" and "decode" is "0010", then you could end up in states "0000" or "0011" if the reset arrives at the lsb of the state early or late. This also applies to "enter", which might be an async input (you'll have to tell us).

For the combinatorial logic, you have to have the signal defined for all code paths. it is common to do:
Code:
process ( signals that you use here ) is
begin
  next_state <= state;
  case state is
  when SOME_STATE =>
    if action = '1' then
      next_state <= OTHER_STATE;
    end if;
   ...
  end case;
end process;
Now if you have action='0' and state = SOME_STATE, there will be logic setting next_state to SOME_STATE.
 

one example would be:
This also applies to "enter", which might be an async input (you'll have to tell us).

yes it is an async input.(a dip switch or a push button).


how can i synchronise the enter input with the clock?
 

Simple -> sample it with a clock (DFF for eg)

or just

--state machine process.
process (current_s,enter,input,a_zero_status) change to process(CLK) and add if rising_edge(CLK) then all this FSM cases
 

did the sampling using a DFF.

but now there is a different problem.

if my program is

in a
mov b,a
in a
add a,b
halt.

i need the two in statements to read two different values.
but now when i give a '1' with dip switch then both my in instructions work simultaneously and i get the same input at both a and b registers.

my project guide said that it may be due to some sparking in the switch which causes multiple rising edges and as the FPGA is very fast both the instructions work at the same time.

please help me with this.

why is this happening?

is there any problem with my code?

pls suggest a method to overcome this.

thanks in advance.
 

If your FPGA clock is like 50 Mhz then there is no way that you will proceed this instructions with a dipswitch cause u got only 20 ns to do it

I made a similar project few years ago for uni and to show that it is HW working i used BCD to show acc value and i slowed my internal clock 10 milins time to have time to change dipswitch.

So u can just add rolling over counter to like 50_000_000 (for 50Mhz clok =1s) that will generate CE for your fpga-microcontroller clock
 


so what clock period should i divide my FPGA clock to?
 

I think 1 second ++ is enought to change dip, it all depands on your reflex ;]
-----------------------------------------------------------------------------------------------------------
now i read what this dipswitch does. U can just do what TrickyDicky says.
 
Last edited:
Instead of checking for the switch to be '1', why not just detect a change from '0' -> '1'?
 

Instead of checking for the switch to be '1', why not just detect a change from '0' -> '1'?

this FSM is later portmapped with a 2 stage DFF to detect the rising edge.

in that also the spark in switch causes multiple rising edges
 

Its probably no spark - have you debounced the swtich?

nope.

after what time will the switch reach a stable state?


BTW debouncing only checks if the switch is in a stable state or not isn't it.

so how can it be used in order to check if there is a 0 to 1 rising edge?
 
Last edited:

debouncing removes the 0/1/0/1 toggling you get when you move a switch, so the idea is after debouncing you get a single clean 0 -> 1 transition.
 

Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…