0

I am writing a LCD controller for an FPGA and am having a really weird (for me at least) problem. The state machine that's supposed to output the needed bits to the screen misbehaves and gets the output pins "stuck" in an old state, while it clearly has moved on to later states.

Here is the relevant parts of the state machine:

PROCESS (clk)
  VARIABLE count: INTEGER RANGE 0 TO clk_divider; -- clk_divider is a generic positive.
BEGIN
 IF (clk'EVENT AND clk = '1') THEN
   count := count + 1;
   IF (count = clk_divider) THEN
     EAUX <= NOT EAUX;
     count := 0;
   END IF;
 END IF;
END PROCESS;

....

PROCESS (EAUX)
BEGIN
  IF (EAUX'EVENT AND EAUX = '1') THEN
    pr_state <= nx_state;
  END IF;
END PROCESS;

....

PROCESS (pr_state)
BEGIN
  CASE pr_state IS
    WHEN EntryMode => --6=1,7=Cursor increment/decrement, 8=Display shift on/off
      RSs <='0';
      DB(7 DOWNTO 0) := "00000110";
      nx_state <= WriteData;
    WHEN WriteData => --Write data to LCD:
      RSs <='1';
      YLED <= '1';
      DB(7 DOWNTO 0) := "01011111";
      i := i + 1;
      IF (i < chars) THEN
        nx_state <= WriteData;
      ELSE
        i := 0;
        nx_state <= ReturnHome;
      END IF;
    WHEN ReturnHome => --Return cursor
      RSs <='0';
      YLED <= '1';
      DB(7 DOWNTO 0) := "01011111";
      nx_state <= WriteData;
  END CASE;
END PROCESS;

Where the bits in the variable DB is assigned to the signal DBOUT:

DBOUT : OUT STD_LOGIC_VECTOR(7 DOWNTO 0) -- In entity
SHARED VARIABLE DB : STD_LOGIC_VECTOR(7 DOWNTO 0) := "00000000"; -- In Architecture
DBOUT <= DB;

DBOUT is outputted (in the .ucf-file) as:

NET "DBOUT(0)" LOC = P10;
NET "DBOUT(1)" LOC = P11;
NET "DBOUT(2)" LOC = P12;
NET "DBOUT(3)" LOC = P13;
NET "DBOUT(4)" LOC = P15;
NET "DBOUT(5)" LOC = P16;
NET "DBOUT(6)" LOC = P18;
NET "DBOUT(7)" LOC = P19;

Using an oscilloscope on the pins I can see that it is clearly stuck outputting the "EntryMode" bits and the "RSs" is set at low, while the YLED (the internal led on the FPGA) is on (it's off at all other states). The really weird thing is (and this took a real long time to find) is that if I change the EntryMode bits from

"00000110"

to

"00000100"

it successfully passes the state and outputs the correct bits. It might be true for other changes as well, but I don't really feel like testing that too much. Any help or tips would be highly appreciated!

UPDATE: After popular request I explicitly put YLED to low in all the early states and switched (back) DB to be a signal. The result is that I can't reach the later states at all, or at least stay in them (even when fiddling with the magic bits, which I guess is a good thing) as the YLED only stays on for a split second after booting the FPGA.

JohanPI
  • 161
  • 9
  • Not enough code to tell. Is this fragment in a proper clocked process, i.e. `if rising_edge(clk) then ... ` ? Also ... did it work correctly in simulation? –  Dec 11 '15 at 14:28
  • Thanks for your comment, will edit my question. But it's in an auxiliary (not sure if the right term) clock. Also, I'm new in the FPGA-field and thus far oscilloscope debugging has been sufficient for me, and so I don't really have run any simulations (although I'm getting the feeling that I should get on that) – JohanPI Dec 11 '15 at 15:24
  • Single clock synchronous design is the simplest approach to a working FPGA. Multiple clocks yield a wealth of nasty problems. And single process state machines are the easiest and most reliable. I'm willing to guess you could rewrite in single process form in much less time than you've spent trying to debug so far. –  Dec 11 '15 at 15:44
  • `DB` is assigned like a variable, so what is the declaration and how it is assigned to an output signal? – Martin Zabel Dec 11 '15 at 15:45
  • YLED is a latch, so you can't know in which state you are; doubly so because YLED is never reset. – FRob Dec 11 '15 at 17:47
  • Brian: Hmm, I have made an ADC controller using the same approach and it didn't have any problems. The reasons that I'm not using the internal clock now is because the LCD display has a much slower one which I have to adapt to. Martin: Will add to the code. FRob: I will need to look into that, but would that be avoided if I set YLED <= '0'; in the earlier states? – JohanPI Dec 11 '15 at 21:13
  • Please avoid using shared variables. I don't know any synthesis tool which handles it always correctly. If `DB` is assigned only in the process shown, then just assign the output signal `DBOUT` instead of `DB`. And yes, you must assign YLED in all control paths of the process, otherwise you get a latch. And again, the synthesis of latches depends on the tool. – Martin Zabel Dec 12 '15 at 13:23
  • I had it as a signal originally but changed it to a shared variable when issues started arising. But I've changed it back now (no difference in the end result by the way). I also added an explicit assignment for the YLED to be low in the early states, and it now seems like the later stages aren't reached after all. – JohanPI Dec 12 '15 at 15:43

1 Answers1

0

There is a complete example, including theory, state machine, and VHDL code on pages 279-290 of "Finite State Machines in Hardware: Theory and Design...", by Volnei Pedroni, MIT Press, Dec. 2013.

VAP
  • 541
  • 3
  • 3
  • Thanks, but it actually is that one (or rather the later, specifically about an LCD controller, one) that I am basing my controller on. – JohanPI Dec 11 '15 at 22:19
  • There is another implementation in a different book by the same author: see chapter 12 of "Circuit Design and Simulation with VHDL", MIT Press, 2010. – VAP Dec 12 '15 at 13:24
  • I believe you are referring to the same book but maybe an earlier edition? In any case that is the chapter I am trying to implement it from. – JohanPI Dec 12 '15 at 15:18
  • They are two very different books (2010 and 2013). That from 2010 is the 2nd edition for a book from 2004, but that from 2013 is different and deals exclusively with finite state machines (implemented with VHDL and also with SystemVerilog). – VAP Dec 12 '15 at 19:48