CSE 370 Introductory Laboratory Assignment

Applying Your Knowledge


Assigned: Monday, November 19, 2007
Due: End of Next Lab Section


Objectives

You have gained many invaluable skills over the course of the quarter; you should now have a good understanding of combinatorial logic, sequential logic, registers, state diagrams, and other related skills. The final two labs of this course, lab eight and nine, will require you to combine all of your skills to develop a complete system on the FPGA. You will be provided with an output device, an LCD screen, and an input device, a magnetic stripe card reader, your task is to get the LCD to display the data stored on magnetic cards swiped through the magnetic card reader. This is not trivial to do, so it is highly suggested that you work with a partner. Furthermore, because of a shortage of LCD screens and magnetic card readers, you should probably work with a partner.

Since this task is relatively complicated it has been broken into two halves for you and your partner to work over the course of two lab sessions and time outside of class. The first half of the lab involves programming the logic that drives the LCD; like many devices the LCD has a series of initializations it must do, and a very specific set of data and commands it understands. For now, we will use the switches on the board to generate the data to be written to the LCD instead of reading data from the magnetic stripe reader. This will allow you to test to see if your LCD behaves as expected.

Before You Begin

The first thing you should before beginning this lab is finding yourself a partner. Make sure you inform your TA who you will be working with so they can check both of you off simultaneously when you have completed this lab.

At your disposal is also the very important datasheet for the LCD: LCD.pdf. This datasheet contains all of the information that is presented below in the lab, though in much greater detail. It is essential that you refer to the datasheet for questions about the LCD. In industry, many times you are presented with a datasheet and asked to develop something based on the information provided in the datasheet. You should take this opportunity in this lab to get some practice reading datasheets and interpreting the data they can provide.

Tasks:

Since this is a complicated lab, this lab write-up will guide you through each component of the lab that you need to create for the final product to work. It is highly suggested you follow the tips and guidelines presented in the lab write-up. However, if you are feeling bold and confident, you are completely welcome to read the datasheet and design the entire lab independently of the write-up. Just make sure you complete all of the check off tasks at the bottom of the lab.
    The Clock:
  1. Now that we have done sequential logic we know how important the clock is for the entire system. So why are we concerned with the clock this time? Why can’t we just use the clock provided by the FPGA? Take a look at the datasheet: LCD.pdf, under the section labeled as "Timing Characteristics" what do you see? Perhaps, more importantly are the units associated with the "Timing Characteristics." The most important time is the Enable Pulse Width takes 450 nano-seconds minimum, however the FPGA’s clock runs at 24Mhz. How fast is that? With that in mind, you should think about making a clock that has a clock cycle that is longer than the 450 nano-second threshold so you don't have to worry about it anymore.

    If this is the problem, then how do we solve this problem? The most logical way would be to slow down the clock since we have no control over the internals of the LCD. Can you think of a way to slow down the clock of the FPGA? Look at the provided Clock_Divider.v file, this verilog file divides down the clockspeed inputted into it and outputs a slower clock. Make sure you understand how it works because it is incomplete. Change the bits parameter and fill in a value for x that slows down the clock enough for the LCD to run properly. Have your TA check you off to confirm that you have the right clock speed chosen.

  2. Now you should create a new design with design flow in Active-HDL. Add a new file called Lab_8.bde. Create an input called: fpga_clk and wire up your Clock Divider on the block diagram design. It should look like the image below when you are finished. Later on when we write up the .qsf file for the pin assignments we will assign the clock provided by the FPGA to the fpga_clk input and use the clk output that the Clock Divider provides as our actual clock.

  3. The Write Button:
  4. For the purposes of testing we will need to use one of the buttons to simulate a clock pulse so we can write characters that we input to the LCD. Since this is only for testing purposes the verilog file has been provided for you here: Pulse_Gen.v. Save it to your src/source folder and wire it up in your bde/block diagram design. Don’t forget to add an in put in your design which represents the button. We will have to remember to assign a button to this input when we write the .qsf file later on. For simplicity, use the name: write_button for the name of the input. When you have wired it up correctly it should look like the image below.

  5. The Reset Button:
  6. We need to be able to reset the LCD to clear the screen whenever we want. The best way to do this is to create a button input that acts as a reset button. Create an input called: reset_button and wire it to an inverter with an output called reset. The inverter is used because on this board you are working with the buttons are actually reversed. When you press the button, instead of sending a logical 1, it sends a logical 0. Once you have wired it up, your diagram should look like the picture below. Well done, you’ve finished all the easy stuff!

  7. The LCD:
  8. Now that we have finished the easy stuff it’s time to actually create the logic that will drive the LCD and get it to do what we want it to do. This information is all contained within the datasheet: LCD.pdf, so if you already have it don’t worry about it. The LCD is capable of many commands; however you will only have to implement two commands: Reset and Clearing the LCD and writing a character to the LCD. To aid you, the pins and what they do are displayed below. The pins are numbered from right to left, when you hold the LCD so that the pins are closest to you.

    PinNameFunction
    1GNDGround - wire GND to it
    2VDD5V - Powers the LCD, wire the 5V red VDD to it
    3Vo1V - Contrast, The LCD needs exactly 1V on this pin so characters are visible
    4RSHigh: When RS is high the LCD expects data to come over the data pins
    Low: When RS is low the LCD expects one of the hard-encoded commands to come over the data pins
    5R/WHigh: When R/W is high the LCD can be read from
    Low: When R/W is low the LCD can be written to
    6EThis is the Enable, it executes command/data is passed to the LCD on the negative edge of E
    7DB0Data Bus 0, this is one bit of data
    8DB1Data Bus 1, this is one bit of data
    9DB2Data Bus 2, this is one bit of data
    10DB3Data Bus 3, this is one bit of data
    11DB4Data Bus 4, this is one bit of data
    12DB5Data Bus 5, this is one bit of data
    13DB6Data Bus 6, this is one bit of data
    14DB7Data Bus 7, this data bit also acts as an output for the LCD. When R/W is high and RS is low, the LCD will output a high signal if the LCD is busy and low signal if the LCD is not busy.

  9. Make sure you understand each one of these pins and exactly what they do. You will have to provide a signal for all 14 of these pins. The first 3 are easy because they are only Ground, VDD, and Vo. To generate a 1V signal for Vo you will be provided with a 3.9K and 1K resistor. You can use these two resistors to divide the voltage down to provide 1V to pin 3 of the LCD. If you don’t understand exactly how to do this, refer to the picture below. You can visit this site: Resistor Color Codings to help you identify which resistor is which. Go ahead and wire up the first 3 pins on your board now. Since the FPGA can arbitrarily assign inputs and outputs to any of the I/O inputs at the bottom of your board you don’t have to worry about wiring your LCD up in any particular way.


  10. LCD Control Logic:
  11. Now that we have set up power, ground, and contrast for the LCD we need to begin to work on the logic that drives the LCD. There are four key signals that you should keep in mind, RS, R/W, E, and Busy. From above you know that RS is driven on Pin 4, R/W is driven on Pin 5, E is driven on Pin 6, and Busy is driven on Pin 14. The goal of the control logic is to pass the correct signal for RS, R/W, and E based on the Busy signal. The idea here is: you can’t write to the LCD if it’s busy, and you can’t pass commands to it either while it’s busy. Note that according to the timing diagrams on the datasheet, you cannot change RS and E at the same time, so keep that in mind when you’re considering your logic. Take a moment to consider what combinations of signals will be necessary to execute the following signals: Reset (User Toggled), Clear Display (Command), Function Set (Command), Display On (Command), Entry Mode Set (Command), Write Character (Data). Make sure you account for the kind of signal and set RS appropriately. Take a look at the waveform below, familiarize yourself with the timings of the LCD control logic.

    Checking Busy Signal Waveform:
  12. Stage 1: During this stage, because we need to read the busy signal, RS needs to be low, and R/W needs to be high. We need to wait one clock cycle before changing E to high to check for the busy signal. The reason why we need to wait is because the specification says there is an address set up time before changing E. Stage 2: During this stage, the address setup time has passed so we can now raise E. Now we can check DataBus[7] for the busy signal. If you see in the waveform, there is a slight delay, this is the propagation delay, which is why it is important your clock is the right length at this point as well. When we finally see DataBus[7]/Busy go low, we can set E back to low since we are no longer interested in the busy signal now that it is low. Stage 3: Since this waveform directly precedes a write sequence, we must drop R/W at this point. Otherwise, R/W can remain whatever you want it to be.
    Character Write Waveform:
    Stage 1: Since we are preparing for a write operation RS has been set to high in this stage, and R/W has been set to low. Afterwards you need to wait for a valid character to appear on the DataBus. When one does, you can create a negative edge on E to write the character. Stage 2: Notice that there is now a valid character sitting on the DataBus. At this point, E is raised because we have confirmed there is a valid character. Stage 3: One clock stage later the state machine can lower E, creating a negative edge. RS and R/W needs to be held for one clock cycle after E is lowered according to the specifications of the LED.
  13. LCD Setup Commands:
  14. As you may have guessed from the previous step, the commands: Reset, Clear Display, Function Set, Display On, Entry Mode Set, and Function Set are the initialization commands that must be passed to the LCD to prepare the LCD to write characters. Since these are the initialization commands, they must be executed in the exact same order every single time the LCD is reset. You should refer to the table below for the execution order, RS signal, and command encoding for each of these commands.

    OrderOperationRSDB[7:0]
    1Clear Display00000 0001
    2Function Set00011 0011
    3Display On00000 1100
    4Entry Mode Set00000 0110
    5+Write Character1???? ????

    Typically you can just send these four commands in succession one after another over the DataBus (Pins 7-14); however it is important to check to make sure the LCD isn’t busy before doing so. This same concept applies for the write command as well, a good rule of thumb is: don’t send anything to the LCD until you know it isn’t busy anymore. Based on the information that you have now, you should be able to design two simple state machines/state diagrams that carries out all of the logic described above. We strongly suggest that you use two state machines that talk between each other. One state machine should check the LCD to see if it is busy or not and pass in the correct RS, R/W and E signals based on whether it is time to send a command or write a character. The other state machine should be responsible for sending the commands during the startup/reset and data bits when the LCD isn’t busy and it is waiting to write. Show your TA your completed state diagrams before moving on.
  15. Programming States in Verilog:
  16. Now that you have created two state diagrams it is time to convert this state diagram into Verilog. The first thing to note is that all of your logic will probably be sequential, so you will want to do most of your coding inside of an always block. Within always blocks you can set up case statements, which allow you to easily transition from state to state. Most of your logic will involve transitioning from one state to another to achieve the desired effect. You can refer to the code below on the usage of case statements in Verilog.

    module case_state_example (CLK, Next, Reset, Fib_Out);
      input CLK, Next, Reset;
      output reg [4:0] Fib_Out;
      reg [2:0] state;

      intial
        state=0;

      always @(posedge CLK) begin
        if (Reset)
          state = 0;
        else case (state)
          0: if (Next) state = 1;
          1: if (Next) state = 2;
          2: if (Next) state = 3;
          3: if (Next) state = 4;
          4: if (Next) state = 5;
          5: if (Next) state = 6;
          6: if (Next) state = 7;
          7: if (Next) state = 0;
        endcase
      end

      always @(state) begin
        case (state)
          0: Fib_Out = 1;
          1: Fib_Out = 1;
          2: Fib_Out = 2;
          3: Fib_Out = 3;
          4: Fib_Out = 5;
          5: Fib_Out = 8;
          6: Fib_Out = 13;
          7: Fib_Out = 21;
        endcase
      end
    endmodule

    If you look over the code, it is actually very redundant, the output given is just the next Fibonacci number in the sequence, which resets or cycles back to the first Fibonacci number. The technique is relatively simple, you only need a register to remember which state you are in, and then a case statement that determines the logic to advance states. You can include an intial block as well to state what value the registers begin with. Another always block can be used to change the output based on the changing state, as the second always block demonstrates in the code. You can use this system to write the logic for your LCD. Try it now, call over a TA to help you if you don’t think you understand.
  17. Tri-State Command and Data:
  18. By now you must have realized that the 8-Pin DataBus is shared by two different sets of signals. One is the command signals to initialize the LCD and the other is the data signals that specify which character to write to the LCD. We need to tri-state these two sets of signals so that they don’t interfere with one another. You can do this in Verilog by specifying the output z, z stands for high impedance, which basically means the connection is temporarily disconnected from the rest of the circuit. You can refer to this article for some more information on Tri-State Logic . You will need to do this when writing the Verilog for your LCD logic. An example is provided for you below on how to Tri-State your command signals and data signals.

    module tri_state_example (Select, Data, DataBus);
        input Select;
        input [7:0] Data;
        output [7:0] DataBus;
        assign DataBus = (Select) ? Data : 8'zzzzzzzz;
    endmodule

    As you can see from the code above, the Select input acts just like a multiplexer, choosing from the Data that comes in or the high impedance z signal. Obviously this code can’t be used directly for your LCD logic because it doesn’t have anything referring to the command signal, however it is a step in the right direction. By setting the DataBus to high impedance z’s in this piece of logic, another module or another piece of code can be free to drive DataBus with a value, possibly the command signals. Keep this in mind when you’re writing your Verilog.
  19. In-Out Buses/Wires
  20. If you have been paying attention you will notice that in your block diagram design you need to make your DataBus an output too because the busy signal comes over on DataBus[7]. If you check under the icon that usually has inputs and outputs, however there is another option to make your input/output an in-out instead. This will allow your code to work when you program it. Just in case you aren’t sure, check the picture below to make sure it looks right.
  21. Testing Your Logic:
  22. Now that you have worked out your logic your LCD logic should consist of 4 inputs and 4 outputs. The four inputs should consist of the clock, reset, write, and an 8-bit switch input. Your four outputs should consist of E, RS, R/W, and an 8-bit data output. Wire up the following three pieces in a new test.bde around your LCD logic as shown in the picture below. You will need these three pieces:

    fpga_clock_sim.v
    lcd_writer_tf.v
    lcd_tf.v

    Make sure that the expected message prints out: "Hello!".
  23. Synthesize and Demonstrate:
  24. Now that everything has been tested and seems to work in simulation you are ready to go ahead and synthesize it and wire it up on your prototyping board. Use SW0-7 to drive your Switch inputs, use the 24MHz clock to drive your clock input that is divided down. Drive your reset with KEY1 and drive your reset with KEY0. You should run your outputs the output pins which you used in the earlier labs. When writing your .qsf file for this lab, you will have to make connections from the LCD pins to the in-out pins on your prototyping board. The name of the pins are written on the label on the side of the in-out pins. These are like "A13, B13, etc." You can refer to this page: In-Out Pins, if you are confused. Make sure everything is working and demonstrate your working LCD to your TA.

Lab Demonstration/Turn-In Requirements

A TA needs to "Check You Off" for each of the tasks listed below.
  1. Have a TA check you off for your Clock Divider.
  2. Have a TA check you off for your LCD Logic State Diagrams.
  3. Have a TA check off your working LCD.

Comments to: cse370- webmaster@cs.washington.edu