Wednesday, December 28, 2011

An implementation of a basic character LCD controller in Verilog

For a while now, I have been wanting to post my code that I developed during my GPS project using a FPGA board. Creating the LCD controller module was the meat of the project and I know many people are interested in how one might approach such a project. I have plans on providing a full-fledged tutorial on how I designed this module, but it is still in the works. In the meantime, I will provide you with the Verilog code which is fairly well commented and with a little cross correlation with the data sheet for the ST7066 the code should be for the most part, self-explanatory. Eventually this post will be edited with a more formal discussion of this code but for now we will have to make due with just the code.

Here is a screenshot of a simulation of what this module does after the initialization phase and you write the ascii value of "C" (0x43) as a data operation. The markers indicate the start and end of the module's execution of this operation.



  1. Do you have a simple example of usage? I'm trying to get something working now and not having much luck, so perhaps I've misunderstood something about how this is supposed to work...

    1. Hi Jules,
      Other than the GPS project I don't have a basic demo. If you follow these guideline this should get it working for you. Keep in mind though that you may need to adjust parameters in the module if you are not using the same exact LCD that I mention.

      1. Instantiate the module.
      2. route the following signals to your top level module and connect (in your user constraints file) the signals to your fpga pins that are wired to your LCD pins.
      3. within a process do the following
      3A. bring RST low when top level reset is not indicated.
      3B. check that the LCD module is ready by checking RDY is high
      3C. setup the DATA and OPER lines
      3D. set ENB high to indicate data is valid
      3E. after the first clock (after setting ENB) you should set OPER back to IDLE to avoid unwanted operations (you technically can wait longer than this but there's not reason to delay this). You can also unset ENB but it is not required.
      3F. Wait for RDY to go high again and repeat the process

      If you look at the GPS example at line 150, this process shows you a basic statemachine method of using this module.

    2. Unfortunately, that's pretty much what I was doing (modulo having adjusted the delay length constants for the fact that I'm operating with a 50MHz clock). It looks as though the timing requirements for your module don't exactly match mine, and this results in the module not registering the data I send it correctly (even, bizarrely, if I increase the length of the delays as specified in your constants to double their original lengths). Looking at your code, I think you're changing the RD/RW and RS registers at the same time as bringing E high, but the datasheet for my module specifies a 60ns delay between these events. On the other hand your code did give me the ideas I needed to fix my own previous attempt: the initialisation sequence is somewhat non-obvious and was not adequately described in the datasheet for my module.

    3. What exact LCD module are you using? What is your clock speed that you are trying to run at? This module should follow the requirements in the ST7066 datasheet, if you look at states 1-11 each have a substate variable that is used for timing control. In substate=0, the current state sets up the LCD_RS and LCD_RW signals to their proper levels and holds for exactly one clock. In the case of the target I used (that used a 24MHz xtal), this was 42ns which meets the LCD module requirements of T_AS >= 30ns. After 42ns, the LCD_E and LCD_DB are set, and the module is held in substate=1 until its corresponding timer flag goes high. Once the timer flag is indicated the module proceeds into substate=2 where it unsets LCD_E and waits for the corresponding processing time period. Once this period elapses it proceeds to the next state.

      I will see if I can dig up my simulation files for this module.

    4. So I posted a picture of a basic simulation above. If I assume that you are using 50MHz for driving the LCD HDL module, then that would mean that your T_AS would be about 20ns which violates both mine and your LCD module specifications. I would recommend dividing your clock down by at least 3 (I would go with 4 just to be safe). A divide by 3 would give a T_AS of 60ns exact. A divide by 4 would give a T_AS of 80ns. Without knowing the exact controller being used I don't know if it is necessary to modify the parameter constants or not, but this is likely the key problem.

    5. The original datasheet was pretty useless, but I've since found one that appears to be for the actual controller used on the module (which is I believe a KS0066 -- the original datasheet stated KS066, which made it somewhat harder to find). This is here: - it seems like your timings would work for me at 5v, but I'm powering it off a 3.3v supply meaning it has a longer "Tsu1" (labelled "Tas" in the ST7066 datasheet). Interestingly, I've found at least one module ( that claims to have a 0ns Tsu1.

      I was running it from a DE0-nano development board, which has a 50MHz oscillator. The cuplrit, pretty clearly therefore, will be the single cycle delay before E is set high; while for you this was over 40ns compared to your 30ns Tas requirement, for me it was 25ns compared to a requirement of 60. I had adjusted the "t_40ns" constant to 2 cycles, but obviously this wasn't enough: it would have needed to be 3 cycles, but as a simplification you weren't actually using it anyway.