Forum Discussion

MDuse's avatar
MDuse
Icon for New Contributor rankNew Contributor
6 years ago

Program not working as expected

Hi,

my device consists of MCU, MAX V CPLD and sensor device. MAX V CPLD is used to interface sensor to MCU.

I have a code in VHDL for MAX V CPLD:

entity Device is
  port (
    clk16: in std_logic; -- 16 MHz clock generated by MCU
    start: inout std_logic; -- start pulse from MCU/operation complete from CPLD. This signal is normally high (pull up resistor). When MCU pulls it down, it tells CPLD to start reading data from sensor. CPLD pulls signal low during read. When it is finished CPLD stops pulling it low.
    clk_out: buffer std_logic; -- output clock for sensor derived from clk16
    start_pulse: out std_logic; -- start pulse for sensor (start command for sensor)
  );
end Device;
 
architecture a of Device is
  type T_STATE is (STOPPED, STARTING, ....another states....);
  signal state: T_STATE := STOPPED;
 
begin
  process (clk16)
  begin
 
    if rising_edge(clk16) then
      clk_out <= not clk_out; -- create clk_out clock
    end if;
			
    if rising_edge(clk_out) then
       case state is
         when STOPPED =>
           if start = '0' then
             start_pulse <= '1';
             state <= STARTING;
             start <= '0' -- sometimes this doesn't happen
           end if;
 
         when STARTING =>
           start_pulse <= '0';
-- code continues here....

The problem is that sometimes line 28 is not "executed" - CPLD doesn't start pulling line low. However, state machine goes to the next state (STARTING) and start_pulse is set to 1 (and then to 0 when it is in STARTING state).

Can you please help me debug this code?

Thanks

Martin

15 Replies

  • sstrell's avatar
    sstrell
    Icon for Super Contributor rankSuper Contributor

    You're doing a check for rising_edge(clk_out) inside a process that is only sensitive to clk16. Your next state logic should be in a separate process, sensitive to clk_out.

    Also, you need to code your bidirectional (or is it supposed to be a tri-state?) correctly. It should look something like this (replace from_core and to_core with what the pin should be when it's an output and when it's an input:

    	start <= from_core WHEN oe='1' 
    		ELSE 'Z'; 
    	to_core <= start;

    There should be some type of output enable control signal for an inout.

    Finally, you're doing an if check on start being 0, but then setting start to 0 as an action in that same if check. I'm not sure what you're trying to do.

    #iwork4intel

  • MDuse's avatar
    MDuse
    Icon for New Contributor rankNew Contributor

    Thanks. I will modify my code.

    Also, you need to code your bidirectional (or is it supposed to be a tri-state?)

    It is open drain output with external pull up resistor and at the same time it is input. Not sure what category should I choose. CPLD should be able to read the line and at the same it should be able to set the line to 0 or high impedance.

    Finally, you're doing an if check on start being 0, but then setting start to 0 as an action in that same if check. I'm not sure what you're trying to do.

    I'm checking, if the MCU pulled start low and if so CPLD pulls the signal also low signalling that it started doing its job. MCU can then release that signal. Later (this is not visible in the code) CPLD sets the start signal to high impedance 'Z' (releasing the line) signalling that it finished the job. Start then goes 1 (as there is external pull up) and MCU detect rising edge which means CPLD finished its job.

    Is it wrong?

  • MDuse's avatar
    MDuse
    Icon for New Contributor rankNew Contributor

    FYI: with scope, I can see that the code doesn't work when MCU pulls the line low exactly at rising edge of clk_out. Then, CPLD correctly detects start = '0' but it doesn't set start to zero (line 28). CPLD then starts doing its job but the start line is released once MCU releases. That is not good as MCU detects rising edge (finished job) when CPLD is actually still working.

  • JohnT_Altera's avatar
    JohnT_Altera
    Icon for Regular Contributor rankRegular Contributor
    Could you provide the full code so that I can further understand your code?
    • MDuse's avatar
      MDuse
      Icon for New Contributor rankNew Contributor
      library IEEE;
      use IEEE.STD_LOGIC_1164.ALL;
      use IEEE.STD_LOGIC_ARITH.ALL;
      use IEEE.STD_LOGIC_UNSIGNED.ALL;
       
      entity Device is
           port (
                 -- SRAM address, data, WE, CE
                 ram_a: out std_logic_vector(14 downto 0);
                 ram_ad: out std_logic_vector(15 downto 0);
      			  ram_we: out std_logic;
      			  ram_ce: out std_logic;
                 -- ADC data output
                 adc_d: in std_logic_vector(7 downto 0);
                 -- main clock generated by MCU
      			  clk16: in std_logic;
                 -- divided clock
      			  clk_out: buffer std_logic;
                 -- open drain output with external pull up resistor. Connected to MCU
      			  start: inout std_logic;
                 -- reset. Connected to MCU
      			  reset: in std_logic
      			  );
           end Device;
       
      architecture b of Device is
           type T_STATE is (STOPPED, STARTING, DUMMY_PERIOD, SAMPLING, FINISHING, WAITING_FOR_STOP);
           type T_SAVINGSTATE is (NOTSAVING, SAVING, FINISHINGSAVING);
      		
      	  signal cnt: unsigned(9 downto 0);
      	  signal address: std_logic_vector(14 downto 0);
      	  signal state: T_STATE := STOPPED;
      	  signal savingstate: T_SAVINGSTATE := SAVING;
           -- timing for state machine:
      	  constant DUMMY_CLOCKS: integer := 97;
      	  constant START_SAVING_CLOCKS: integer := DUMMY_CLOCKS + 5;
      	  constant STOP_SAMPLING_CLOCKS: integer := DUMMY_CLOCKS + 512;
      	  constant FINISH_SAVING_CLOCKS: integer := START_SAVING_CLOCKS + 512;
      	  constant STOP_SAVING_CLOCKS: integer := FINISH_SAVING_CLOCKS + 1;
      begin
       
           process (clk16, clk_out, reset)
           begin
      	      if reset = '0' then
      			    address <= (others => '0');
      				 state <= STOPPED;
      				 savingstate <= NOTSAVING;
      				 ram_a <= (others => 'Z');
      			    ram_ad <= (others => 'Z');
      				 ram_ce <= 'Z';
      				 ram_we <= 'Z';
      				 clk_out <= '0';
      				 start_pulse <= '0';
      				 start <= 'Z';
      			else
       
      				if rising_edge(clk16) then
      					 clk_out <= not clk_out;
      				end if;
      				
      				if falling_edge(clk16) then
      					 if savingstate = SAVING then
      						 if clk_out = '1' then
      							 ram_a <= address;
      							 ram_ad(7 downto 0) <= adc_d;
      						 else
      						    address <= unsigned(address) + 1;
      							 ram_ad(15 downto 8) <= adc_d;
      						 end if;
      					 elsif savingstate = NOTSAVING then
      					    ram_a <= (others => 'Z');
      					    ram_ad <= (others => 'Z');
      					 end if;
      				end if;
       
      				if rising_edge(clk_out) then
      				   case state is
                        when STOPPED =>
                           if start = '0' then -- MCU started the job by pulling the line low
      								start <= '0'; -- pull it low to tell MCU the job is in progress
      								state <= STARTING;
      							end if;
                        when STARTING =>
      							cnt <= (others => '0');
      						   state <= DUMMY_PERIOD;
      						when DUMMY_PERIOD =>
      						   cnt <= cnt + 1;
      						   if cnt = DUMMY_CLOCKS then
      							   state <= SAMPLING;
      						   end if;
      					   when SAMPLING =>
      						   cnt <= cnt + 1;
      						   if cnt = START_SAVING_CLOCKS then
      							   savingstate <= SAVING;
      						   elsif cnt = STOP_SAMPLING_CLOCKS then
      							   state <= FINISHING;
      						   end if;
      						when FINISHING =>
      						   cnt <= cnt + 1;
      						   if cnt = FINISH_SAVING_CLOCKS then
      							   savingstate <= FINISHINGSAVING;
      							elsif cnt = STOP_SAVING_CLOCKS then
      							   savingstate <= NOTSAVING;
      								state <= WAITING_FOR_STOP;
      								start <= 'Z'; -- job done, release the line
      							end if;
      						when WAITING_FOR_STOP =>
      						   cnt <= cnt + 1;
      						   if cnt = STOP_SAVING_CLOCKS + 100 then
      							   state <= STOPPED;
      							end if;
                     end case;
       
      				end if;
      				
      				if savingstate = SAVING then
      				   ram_ce <= cis_cp;
      			   elsif savingstate = FINISHINGSAVING then
      					ram_ce <= '1';
      				else
      				   ram_ce <= 'Z';
      			   end if;
       
               end if;
           end process;         
       
      end b;

      There is my MAX V that implements this code, ADC that converts data from sensor, MCU and SRAM.

      MCU resets or starts the job (reset, start),.

      When started, CPLD starts reading data from ADC's 8 bit data bus and saves it to SRAM which has 16 bit data bus.

  • JohnT_Altera's avatar
    JohnT_Altera
    Icon for Regular Contributor rankRegular Contributor
    Hi, May I know if there is any reason that you are putting the "Start" as bidirectional? Will there be any issue if you are driving '0' and the MCU is driving '1'? I think there is some issue on the implementation where you are not implementing it correctly in your design. The other thing is that you are waiting for "Start" to be '0' before you drive it to '0'. I do not think that Quartus is synthesis the code correctly as there will be a confusion here.
    • MDuse's avatar
      MDuse
      Icon for New Contributor rankNew Contributor

      Let me explain the start signal (for now I renamed it start/finished as it better describes it purpose).

      Here is schematic:

      Normally, no chip is pulling the line low - both chips sets the pin with start/finished line to high impedance input. When MCU wants CPLD to start its job it configures start/finished pin to output and writes 0 to it (pulls it low) for at least one clock period so the CPLD is able to catch that. CPLD detects this "start condition" and pulls the line low also to tell MCU that it is working. When the job is done, it again configures start/finished pin to high impedance input which means rising edge will happen on the line (as no chip is pulling it low) which can be detected by MCU...

      If what I have written can't be implemented in CPLD then that is good news for me as it means the problem is solved. I can move start <= '0'; to the next state (that happens just one clock period after, that is perfectly OK) and extend MCU's start pulse to 2 clock periods (so there is no rising edge meaning false job done). But of course, I would like to be sure that this is the problem.

  • JohnT_Altera's avatar
    JohnT_Altera
    Icon for Regular Contributor rankRegular Contributor
    Hi, The Max V IO pins support open drain but it will depending on you implement it. You should be able to look into your Quartus fitter result to see if the pin is configured as open drained. Have you tried to performed simulation to see if you are implementing it correct?
    • MDuse's avatar
      MDuse
      Icon for New Contributor rankNew Contributor

      This is pinout for start signal (output of Fitter):

      Pin Name/Usage : Location : Dir. : I/O Standard : Voltage : I/O Bank : User Assignment

      start: 44 : bidir : 3.3-V LVTTL : : 2 : Y

      I simulated it and was not able to reproduce the issue. I tried to move falling edge of start signal relative to clk_out clock to several positions when simulating, but the issue never happened when simulating.

    • MDuse's avatar
      MDuse
      Icon for New Contributor rankNew Contributor

      But it is bidir which should be good for me as long as CPLD can set bidir pin to 0 and to high impedance state and is able to read its state.

      So, can you confirm, that this implementation that is present in my code:

      if start = '0' then
         start <= '0'; 
      end if;

      is bad idea and I should do it in some official way?

  • JohnT_Altera's avatar
    JohnT_Altera
    Icon for Regular Contributor rankRegular Contributor
    Hi, Then I would recommend you to use OE to control whether to drive '0" or high Z.
    • MDuse's avatar
      MDuse
      Icon for New Contributor rankNew Contributor

      Yes, I will do it this way and perform some testing to see if that was the cause of my problem.

      Thank you

    • MDuse's avatar
      MDuse
      Icon for New Contributor rankNew Contributor

      I modified my VHDL and now testing. It seems it works, RTL looks simpler with new version of code but we have to monitor it for longer time. The issue happened months after we were using original code and only with two devices.