Programmable Devices
CPLDs, FPGAs, SoC FPGAs, Configuration, and Transceivers
20638 Discussions

Program not working as expected

MDuse
Beginner
1,102 Views

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

 

 

 

0 Kudos
15 Replies
sstrell
Honored Contributor III
706 Views

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

0 Kudos
MDuse
Beginner
706 Views

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?

0 Kudos
MDuse
Beginner
706 Views

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.

0 Kudos
JohnT_Intel
Employee
706 Views
Could you provide the full code so that I can further understand your code?
0 Kudos
MDuse
Beginner
706 Views
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.

 

 

0 Kudos
JohnT_Intel
Employee
706 Views
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.
0 Kudos
MDuse
Beginner
706 Views

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

 

Here is schematic:cpld.png

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.

0 Kudos
JohnT_Intel
Employee
706 Views
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?
0 Kudos
MDuse
Beginner
706 Views

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.

0 Kudos
JohnT_Intel
Employee
706 Views
I didn't observed that the open drain is enabled. Usually you need to design it where you can either follow the guide in https://www.intel.com/content/www/us/en/programmable/support/support-resources/knowledge-base/solutions/rd06252007_878.html or use the IP in Quartus (https://www.intel.com/content/dam/www/programmable/us/en/pdfs/literature/ug/ug_altiobuf.pdf)
0 Kudos
MDuse
Beginner
706 Views

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?

0 Kudos
JohnT_Intel
Employee
706 Views
Hi, Then I would recommend you to use OE to control whether to drive '0" or high Z.
0 Kudos
MDuse
Beginner
706 Views

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

 

Thank you

0 Kudos
JohnT_Intel
Employee
706 Views
Sure. I will wait for your update
0 Kudos
MDuse
Beginner
706 Views

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.

0 Kudos
Reply