FPGA evaluation cards, like the Xilinx VC-707, often have 16x2 LCD panels. These display up to two lines of 16 characters each.
There are numerous examples online showing how to exercise these displays using low-level RTL. It is much easier with a HLS flow such as Kiwi, where the thread of control with method call inlining neatly generates the same designs.
The standard 16x2 DisplayTech LCD is documented here https://www.openhacks.com/uploadsproductos/eone-1602a1.pdf.
The following CSharp file was compiled with mono mcs.
// // Kiwi Scientific Acceleration // (C) 2009 DJ Greaves - University of Cambridge, Computer Laboratory // // Demonstration of driving the 16x2 LCD display panel found on FPGA development boards. // using System; using KiwiSystem; class KiwiLCD1602Driver { /* // Put the following padring code in a Xilinx top-level file. // Instance of the Kiwi-generated LCD driver wire lcd_data4_openable; wire [3:0] lcd_data4_in, lcd_data4_out; bufif1(LCD_DB4_LS, lcd_data4_out[0], lcd_data4_openable); bufif1(LCD_DB5_LS, lcd_data4_out[1], lcd_data4_openable); bufif1(LCD_DB6_LS, lcd_data4_out[2], lcd_data4_openable); bufif1(LCD_DB7_LS, lcd_data4_out[3], lcd_data4_openable); IBUF lcd_in40(lcd_data4_in[0], LCD_DB4_LS); IBUF lcd_in41(lcd_data4_in[1], LCD_DB5_LS); IBUF lcd_in42(lcd_data4_in[2], LCD_DB6_LS); IBUF lcd_in43(lcd_data4_in[3], LCD_DB7_LS); kiwi_lcd1602_driver the_lcd_driver( .LCD_RS_LS(LCD_RS_LS), .LCD_E_LS(LCD_E_LS), .LCD_RW_LS(LCD_RW_LS), .lcd_data4_in(lcd_data4_in), .lcd_data4_out(lcd_data4_out), .lcd_data4_openable(lcd_data4_openable) ); */ // The standard 1602a DisplayTech LCD is documented here https://www.openhacks.com/uploadsproductos/eone-1602a1.pdf // http://embeddedlifehelp.blogspot.co.uk/2012/03/16x2-lcd-programming-for-beginners-made.html // https://learningmsp430.wordpress.com/2013/11/16/16x2-lcd-interfacing-in-4-bit-mode/ // Use 4-bit, tri-state connection to the LCD panel. [Kiwi.OutputBitPort("LCD_RS_LS")] static bool LCD_RS; [Kiwi.OutputBitPort("LCD_RW_LS")] static bool LCD_RW; [Kiwi.OutputBitPort("LCD_E_LS")] static bool LCD_E; [Kiwi.OutputBitPort("lcd_data4_openable")] static bool lcd_data4_openable; [Kiwi.InputWordPort(3, 0, "lcd_data4_in")] static byte lcd_data4_in; [Kiwi.OutputWordPort(3, 0, "lcd_data4_out")] static byte lcd_data4_out; public static void PortIdle() { lcd_data4_openable = true; LCD_RS = false; LCD_RW = false; LCD_E = false; lcd_data4_out = 0; } // To send a byte on the 4-bit bus, send high nibble first. // To read a byte on the 4-bit bus, get high nibble first. static byte readByte(bool dataf) { LCD_RS = dataf; // Low for control, high for readback of data. lcd_data4_openable = false; LCD_RW = true; Kiwi.Pause(); LCD_E = true; Kiwi.Pause(); byte r = (byte)(lcd_data4_in & 0xF); LCD_E = false; Kiwi.Pause(); LCD_E = true; Kiwi.Pause(); r = (byte)((r << 4) | (lcd_data4_in & 0xF)) ; LCD_E = false; Kiwi.Pause(); lcd_data4_openable = true; // Re-enable outputs for next write. return r; } // To send a byte on the 4-bit bus, send high nibble first. static void sendByte(bool dataf, byte data) { LCD_RS = dataf; // Low for control, high for readback of data. lcd_data4_openable = true; LCD_RW = false; lcd_data4_out = (byte)((data >> 4) & 0xF); Kiwi.Pause(); LCD_E = true; Kiwi.Pause(); LCD_E = false; Kiwi.Pause(); lcd_data4_out = (byte)((data >> 0) & 0xF) ; Kiwi.Pause(); LCD_E = true; Kiwi.Pause(); LCD_E = false; Kiwi.Pause(); } static void WaitNotBusy() { // Have two pauses to give it a chance to go busy. Kiwi.Pause(); Kiwi.Pause(); // Busy flag is bit seven of the status register - poll to be not busy. while (true) { byte status_reg = readByte(false); Kiwi.Pause(); if (((int)(status_reg) & 128)==0) break; } } static void wait_delay_40() { for (int k=5; k != 0; k--) Kiwi.Pause(); // Wait 5*period for 4.1 ms or 100 us } static void LcdCmdD(byte cmd, int delay) // Send a command byte using a delay. { sendByte(false, cmd); Console.WriteLine("LCD sent cmd byte using delay timing alue={0:X}", cmd); } static void LcdCmd(byte cmd) // Send a command byte using polled busy. { WaitNotBusy(); wait_delay_40(); sendByte(false, cmd); Console.WriteLine("LCD sent cmd byte value={0:X}", cmd); } public static void sendDataByte(byte data) { WaitNotBusy(); wait_delay_40(); sendByte(true, data); Console.WriteLine("LCD sent data byte (datareg={1}) value={0:X}", data, LCD_RS); } public static void Reset() { lcd_data4_openable = false; Kiwi.Pause(); PortIdle(); wait_delay_40(); // Wait >4.1ms and >100 us in the gaps between the first 2 '3' operations, then used BF polling instead of delays. LcdCmdD(0x33, 2000); // Reset code 30 wait_delay_40(); LcdCmdD(0x32, 2000); // Reset code 20 wait_delay_40(); LcdCmdD(0x28, 2000); // 4-bit interface, 2 lines. wait_delay_40(); } public static void Setup() { LcdCmd(0x06); // Entry mode, ID=1, S=0; LcdCmd(0x0f); // Display: disp on, cursor on, blink on. LcdCmd(0x01); // Home LcdCmd(0x80); // Set data data RAM location 0 of top line write } public static void WriteString(string msg) { char [] cdata = msg.ToCharArray(); for (int x=0; x<msg.Length; x++) sendDataByte((byte)cdata[x]); } } class tester { [Kiwi.OutputBitPort("done")] static bool done; [Kiwi.InputBitPort("select")] static bool select; public static void Main() { select = false; RunHW(); } [Kiwi.HardwareEntryPoint()] public static void RunHW() { done = false; Console.WriteLine("Hello from LCD 1602 driver"); Kiwi.Pause(); Kiwi.Pause(); Kiwi.Pause(); KiwiLCD1602Driver.PortIdle(); KiwiLCD1602Driver.Reset(); KiwiLCD1602Driver.Setup(); Kiwi.Pause(); string msg = (select) ? "Hello World": "David Greaves"; KiwiLCD1602Driver.WriteString(msg); Kiwi.Pause(); while(true) { done = true; Kiwi.Pause(); } } } // eof
gmcs kiwi-lcd1602-driver.cs -r:/home/djg11/d320/hprls/kiwipro/kiwic/distro/support/Kiwi.dll kiwi-lcd1602-driver.cs(20,49): warning CS0414: The private field `KiwiLCD1602Driver.LCD_RW' is assigned but its value is never used kiwi-lcd1602-driver.cs(21,48): warning CS0414: The private field `KiwiLCD1602Driver.LCD_E' is assigned but its value is never used kiwi-lcd1602-driver.cs(24,58): warning CS0414: The private field `KiwiLCD1602Driver.LCD_DATA' is assigned but its value is never used kiwi-lcd1602-driver.cs(97,44): warning CS0414: The private field `tester.done' is assigned but its value is never used Compilation succeeded - 4 warning(s) /home/djg11/d320/hprls/kiwipro/kiwic/distro/bin/kiwic -give-backtrace -vnl-rootmodname=DUT -vnl=kiwi-lcd1602-driver.v kiwi-lcd1602-driver.exe -vnl-resets=synchronous -kiwic-cil-dump=combined -kiwic-kcode-dump=enable -res2-loadstore-port-count=0 -vnl-roundtrip=disable devx (getenv "HPRLS_DEVX"="1") developer mode=true +++ devx: other form in bifo GetLength pokidl: CT_arr (CTL_net (false,16,Signed,[Cil_at_native]),Some 11L) +++ devx: other form in bifo GetLength pokidl: CT_arr (CTL_net (false,16,Signed,[Cil_at_native]),Some 11L) +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'3I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'0I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'3I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'0I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'2I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'12I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'0I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'8I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'0I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'1I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'0I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'6I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'0I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'12I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := C8u(15&(C8u("Hello World"[TKWr0.8_V_1]))>>4): assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := C8u(15&(C8u("Hello World"[TKWr0.8_V_1]))): assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'3I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'0I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'3I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'0I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'2I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'12I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'0I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'8I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'0I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'1I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'0I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'6I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'0I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'12I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := C8u(15&(C8u("Hello World"[TKWr0.8_V_1]))>>4): assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := C8u(15&(C8u("Hello World"[TKWr0.8_V_1]))): assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'3I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'0I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'3I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'0I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'2I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'12I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'0I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'8I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'0I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'1I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'0I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'6I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'0I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := U8'12I: assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := C8u(15&(C8u("Hello World"[TKWr0.8_V_1]))>>4): assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ wrap check warning: LCD_DATA_LS[3:0]:OUTPUT::Unsigned{init=0, io_output=true, username=LCD_DATA_LS, HwWidth=4, storage=8} := C8u(15&(C8u("Hello World"[TKWr0.8_V_1]))): assignment may wrap differently: rhs/w=8, lhs/w=4, store/w=8I +++ devx: wrap_unaltered: no arg nn in net $$AUTOFORMAT: This will be automatically replaced with a printf formatted string. iverilog kiwi-lcd1602-driver.v vsys.v ./a.out VCD info: dumpfile vcd.vcd opened for output. Hello from LCD 1602 driver LCD sent cmd byte (datareg=0) value=33 LCD sent cmd byte (datareg=0) value=33 LCD sent cmd byte (datareg=0) value=32 LCD sent cmd byte (datareg=0) value=2c LCD sent cmd byte (datareg=0) value=8 LCD sent cmd byte (datareg=0) value=1 LCD sent cmd byte (datareg=0) value=6 LCD sent cmd byte (datareg=0) value=c LCD sent data byte (datareg=1) value=48 LCD sent data byte (datareg=1) value=65 LCD sent data byte (datareg=1) value=6c LCD sent data byte (datareg=1) value=6c LCD sent data byte (datareg=1) value=6f LCD sent data byte (datareg=1) value=20 LCD sent data byte (datareg=1) value=57 LCD sent data byte (datareg=1) value=6f LCD sent data byte (datareg=1) value=72 LCD sent data byte (datareg=1) value=6c LCD sent data byte (datareg=1) value=64 Process make finishedDownload
Src files in this ZIP.
Archive: kiwi_lcd1602_driver.zip Length Date Time Name --------- ---------- ----- ---- 7071 2016-06-30 10:44 cbguart1.v 5796 2016-08-08 16:19 lcd_panel_top.v 1863 2016-08-08 16:23 simsys.v 908 2016-06-17 16:45 vsys.v 6210 2016-08-08 16:19 kiwi_lcd1602_driver.cs 2453 2016-06-22 14:22 lcd-panel.tcl 888 2016-08-08 16:22 Makefile 3437 2016-08-01 15:50 lcd-panel.xdc --------- ------- 28626 8 files
Updated April 2016 UP.