Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

VHDL that works in simulation does not work in hardware.

Status
Not open for further replies.

kureigu

Member level 2
Member level 2
Joined
Jan 14, 2013
Messages
49
Helped
0
Reputation
0
Reaction score
0
Trophy points
1,286
Location
Scotland
Visit site
Activity points
1,779
I've written a few components to move a stepper motor back and forwards. I've simulated it in modelsim and it works as expected, but it won't work the same in hardware at all.

Basically I have a motor driving component, which takes a command of number of steps, hold time and speed and then performs the movement. Then I have the control_arbiter, which is just an intermediate bridge that connects components wanting access to the motors and the motor driving components.
Finally I have a 'search pattern' component, which basically issues the commands to move the motor back and forth.

My problem is that I can't seem to get direction to change when it's running in hardware, regardless of it working in simulation.

Any help with this would be greatly appreciated

Motor driver:

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


    entity motor_ctrl is

	port(	clk: in std_logic;
	
			-- Hardware ports
			SCLK, CW, EN: out std_logic;	-- stepper driver control pins
			
			-- Soft ports
			Speed, steps: in integer;	
			dir: in std_logic;			-- 1 = CW; 0 = CCW;
			hold_time: in integer;		-- if > 0, steppers will be held on for this many clock periods after moving
			ready: out std_logic;		-- indicates if ready for a movement
			activate: in std_logic;		-- setting activate starts instructed motion. 
			pos_out: out integer			-- starts at 2000, 180deg = 200 steps, 10 full rotations trackable
			);

    end motor_ctrl;


    architecture behavioural of motor_ctrl is

	type action is (IDLE, HOLD, MOVE);
	signal motor_action: action := IDLE;

	signal clk_new: std_logic;
	signal position: integer := 2000;
	
	signal step_count: integer := 0;
	signal drive: boolean := false;

    begin

	-- Clock divider
	clk_manipulator: entity work.clk_divide port map(clk, speed, clk_new);

	-- Drive motors
	with drive select
		SCLK <= clk_new when true,
						'0' when false;
	
	pos_out <= position;
	
	process(clk_new)
		-- Counter variables
		variable hold_count: integer := 0;		
	begin
	
		if rising_edge(clk_new) then
			case motor_action is
				
				when IDLE =>
					-- reset counter vars, disable the driver and assert 'ready' signal
					hold_count := 0;
					step_count <= 0;
					drive <= false;
					EN <= '0';
					ready <= '1';
					
					-- When activated, start moving and de-assert ready signal
					if(activate = '1') then
						motor_action <= MOVE;
					end if;
					
				when HOLD =>
					-- Stop the step clock signal
					ready <= '0';
					drive <= false;	
					-- Hold for given number of clock periods before returning to idle state
					if(hold_count = hold_time) then
						motor_action <= IDLE;
					end if;
					-- increment counter
					hold_count := hold_count + 1;
					
				when MOVE =>					
					-- Enable driver, apply clock output and set direction
					ready <= '0';
					EN <= '1';
					drive <= true;
					CW <= dir;		
					
					-- track the position of the motor
					--if(dir = '1') then	
					--	position <= steps + step_count;
					--else
					--	position <= steps - step_count;
					--end if;
					
					-- Increment count until complete, then hold/idle
					if(step_count < steps-1) then
						step_count <= step_count + 1;
					else
						motor_action <= HOLD;
					end if;
								
			end case;
		end if;
		
	end process;

	
    end behavioural;


Control_arbiter:

Code:
    entity Control_arbiter is
	
	port (clk: in std_logic;
			EN, RST, CTRL, HALF, SCLK, CW: out std_logic_vector(2 downto 0)
			-- needs signals for levelling and lock
			);
			
    end Control_arbiter;
 
 
    architecture fsm of Control_arbiter is

	type option is (INIT, SEARCH);
	signal arbitration: option := INIT;
	
	-- Motor controller arbiter signals
	-- ELEVATION
	signal El_spd, El_stps, El_hold, El_pos: integer;
	signal El_dir, El_rdy, El_run: std_logic;

	
	-- Search signals
	signal search_spd, search_stps, search_hold: integer; 
	signal search_dir, search_Az_run, search_El_run: std_logic := '0';
	-- status
	signal lock: std_logic := '0';
	
    begin

	-- Motor controller components
	El_motor: entity work.motor_ctrl port map(clk, SCLK(0), CW(0), EN(0),
															El_spd, El_stps, El_dir, El_hold, El_rdy, El_run);														
	
	
	-- Search component
	search_cpmnt: entity work.search_pattern port map(	clk, '1', search_dir, search_stps, search_spd, search_hold, 
																		El_rdy, search_El_run);
			
			
	process(clk, arbitration)
	
	begin
	
		if rising_edge(clk) then
		
			case arbitration is
			
				when INIT =>
					-- Initialise driver signals
					EN(2 downto 1) <= "11";
					CW(2 downto 1) <= "11";
					SCLK(2 downto 1) <= "11";
					RST  <= "111";
					CTRL <= "111";
					HALF <= "111";
					-- Move to first stage
					arbitration <= SEARCH;
					
				when SEARCH =>
					-- Map search signals to motor controllers
					El_dir  <= search_dir;
					El_stps <= search_stps;
					El_spd  <= search_spd;
					El_hold <= search_hold;
					El_run  <= search_El_run;
			
			end case;
			
		end if;
	
	end process;

	
    end fsm;

Search Pattern:

Code:
    entity search_pattern is

	generic (step_inc: unsigned(7 downto 0) := "00010000"
				);
	port (clk: in std_logic;
			enable: in std_logic;
			dir: out std_logic;
			steps, speed, hold_time: out integer;
			El_rdy: in std_logic;
			El_run: out std_logic
			);

    end search_pattern;


    architecture behavioural of search_pattern is

	type action is (WAIT_FOR_COMPLETE, LATCH_WAIT, MOVE_EL_CW, MOVE_EL_CCW);
	signal search_state: action := WAIT_FOR_COMPLETE;
	signal last_state: action := MOVE_EL_CCW;

    begin

	hold_time <= 1; 
	speed <= 1;
	steps <= 2;
	
	process(clk)
	
	begin
			
		if rising_edge(clk) then
		
			-- enable if statement
			
				case search_state is
				
					when LATCH_WAIT =>
						-- Make sure a GPMC has registered the command before waiting for it to complete
						if(El_rdy = '0') then		-- xx_rdy will go low if a stepper starts moving
							search_state <= WAIT_FOR_COMPLETE;		-- Go to waiting state and get ready to issue next cmd
						end if;
				
					when WAIT_FOR_COMPLETE =>

						-- Wait for the movement to complete before making next
						if(El_rdy = '1') then		
							-- Choose next command based on the last
							if last_state = MOVE_EL_CCW then
								search_state <= MOVE_EL_CW;
							elsif last_state = MOVE_EL_CW  then
								search_state <= MOVE_EL_CCW;
							end if;
						end if;				

					when MOVE_EL_CW =>
						dir <= '1';
						El_run <= '1';
						last_state <= MOVE_EL_CW;
						search_state <= LATCH_WAIT;

					when MOVE_EL_CCW =>
						dir <= '0';
						El_run <= '1';
						last_state <= MOVE_EL_CCW;
						search_state <= LATCH_WAIT;
						
					when others =>
						null;
				end case;

			-- else step reset on not enable
				
		end if;
	
	end process;
	
    end behavioural;

Simulation attached
 

Attachments

  • wave.PNG
    wave.PNG
    12.3 KB · Views: 196

-- Clock divider

Thats probably your problem. Logic clock dividers are not recommended because you can screw with timings. You're much better off, and more likely to have a working design, if you use clock enables instead of a logic divided clock.
 

I'm sorry, I don't understand how I could use clock enables to create a frequency divider with an adjustable speed. Seems like it would be useful to know though even if it turns out not to be the solution.
 

basically in the same way you have created your clock divider. for a divide by 2, just set the clock enable high for 1 in every 2 clock, 1/5 set it high 1 in 5 clocks etc.
 

So essentially you still need to count the clock edges. I don't see the difference between what you're saying and what I'm doing in my clock divider:

Code:
entity clk_divide is
	port(	clk: in std_logic := '0';
			FREQ_DIV: in integer := 250000;
			clk_out: out std_logic
			);
end clk_divide;


architecture behaviour of clk_divide is

	signal clk_outX: std_logic := '0';
	signal count: natural := 0;		-- Keep track of cycles

begin
	
	process(clk)
	begin
		
		if rising_edge(clk)then
			count <= count+1;
			if count > FREQ_DIV then
				count <= 0;
				clk_outX <= not clk_outX;
			end if;
		end if;
	
		clk_out <= Clk_outX;
	
	end process;
			

end behaviour;
 

with a logic clock, you have to connect the clock to the clock inputs on the reigsters in the device. Because its logic generated, it wont neccesarily be routed onto one of the clock networks so you will have skew problems.
With clock enables, they just connect to the enable pin on the registers, and you dont have a skew problem.
 

I'm really sorry, I still don't follow what you're saying.
Can you possibly provide an example to demonstrate the difference?
 

its to do with the technology, not the code.

Each register in an fpga has Clk, async reset, D and enable inputs. On Xilinx they have an Sreset input too. In the fabric of the chip there are low skew clock networks to route the clock around the chip so that all registers recevieve the clock within pico seconds of each other.

If you connect the clk pin to some logic generated clock that uses the normal routing that is not low skew, the registers will receive the clock all at different times, screwing up your design. This skew will be heat dependent, so will not be constant. If they all have the same clock, then all the logic is clocked together - with enables controlling when the registers should work or not. It is all synchronous so there are no timing problems.
 
  • Like
Reactions: Mwnuk

    Mwnuk

    Points: 2
    Helpful Answer Positive Rating
Ok, I understand that much, but still don't see how I'm supposed to ensure that I'm avoiding these issues.

Irrespective of this, however, I think we're veering off topic here, and I'm not convinced that this is the actual problem with my code. I've tested the design after removing the clock divider completely and using only one main clock input and I get the same issue.
 

does your testbench test all of the cases in real life? have you actually tested for failure - eg. can your SM get locked up?
 

It will run through every state in the testbench, and I can deduce from the signals that it is successfully entering and leaving each in the order I'd expect.
I think, however, in hardware, the 'ready' flag might not be being set again once the movement is complete, in which case the motor control will execute the same task over and over, never allowing search_pattern to get out of it's 'wait_for_complete' state and change the direction after the first iteration. It definitely makes it to the 'MOVE_EL_CW' state at least once because the direction of motion always matches what 'dir' is set to in that state.
 

Where is the top level entity/architecture that ties this all together?

One potential problem is the derived clock, you have:
with drive select SCLK <= clk_new when true, '0' when false;
...
process(clk_new)
...
begin
if rising_edge(clk_new) then
That process depends on signals that look like they are generated within the 'clk' domain but are being used in the 'clk_new' domain which is skewed from 'clk'. It is quite likely that those signals that are changing state and violating the setup or hold time relative to 'clk_new' and causing problems.

- Did you run static timing analysis and did it pass?
- If it did pass, do you have some setting in the tool that is inhibiting doing the timing analysis on signals that cross clock boundaries? If so, you need to disable that setting so that the signals that go through a path starting in 'clk' and end up in 'clk_new' (and vice versa) are analyzed.

Kevin Jennings
 

Where is the top level entity/architecture that ties this all together?

Right now I'm testing in both hardware and simulation with control_arbiter as the top level. The clock is the only input, once it starts the state machines run off each other's handshaking (rdy, run signals).

How would I avoid the signals changing state then? I tried running motor_ctrl's process off clk directly, bypassing the clock divider and i saw no change in how it operated in hardware.

I haven't run static timing analysis. I'm not sure how I would go about that, I'm not familiar with the timing analyzer at all I'm afraid.
 

How would I avoid the signals changing state then? I tried running motor_ctrl's process off clk directly, bypassing the clock divider and i saw no change in how it operated in hardware.

Do this only in simulation first. When you have it working using ONLY clk and clk_enables, then move on to hardware.
 

I've now implemented a clock enable as you advised, but I still see the exact same results.

If anyone is bored and feel like a challenge, then they're more than welcome to have a play around with the files: https://www.dropbox.com/s/4jru4rqovhpqpkl/TB_search_pattern.zip

Any and all suggestions are welcome at this point. Even if it means a total re-write or restructure. I also currently welcome people to personally come and put me out of misery because this issue has dragged on so long I have literally lost the will to live, although I am very grateful for the time everyone here has spent in helping me so far.
 

I don't have a testbench file as such, I just compiled it in modelsim and created a waveform for the clock and ran it whenever I needed to simulate it.
 

I highly suggest you create a testbench. That would massively help your debugging before you go near the hardware.
 

Status
Not open for further replies.

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top