Saturday, 22 December 2012

Scrolling or Moving Text Using 7 Segment LED's in Verilog | FPGA

This one is going to be a quick easy project. The objective here is to get use the 7-segment LED display on the board to display a scrolling text. it should be slow enough so that it can be easily read.

I have made the delay between each word shift to be 1 second. This is long enough to read the text comfortably without missing anything. I have explained in my previous post how to create an accurate delay using verilog, you can read about it there: Create accurate delay in Verilog. Now since I need a 1 second delay here the counter monitoring this count must count to 50,000,000. And the register needed to hold this count must be 29 bits wide.

Using this method you can scroll and display anything. Here I have chosen to display the text "Hello There" to scroll across the LED's and appear to be continuous without any stops. The concept is pretty simple. I create a separate counter that increments every time a count of 50,000,000 is reached. This counter will count till 8, display the entire text, and then reset back to 0 so that the text can be scrolled again. For every count increment it will match it with a case statement. This case will instruct the display for that second.

Now since 7 segment LED's are being used here you must have a proper understanding of them before trying to read and understand the code. I have detailed the topic in detail here: 7 Segment LED Multiplexing in Verilog. Be sure to read and understand it.

So instead of displaying numbers the plan is to display letters on the LED's. This can be achieved by changing the case statement to our requirements.

As usual the code is commented in detail and should be self explanatory even without the above rant. A video demonstration has been added at the end.

module scrolling_name(
    input clock,
    input reset,
    output a,
    output b,
    output c,
    output d,
    output e,
    output f,
    output g,
    output dp,
    output [3:0] an
    );

reg [28:0] ticker; //to hold a count of 50M
wire click;
reg [3:0] fourth, third, second, first; // registers to hold the LED values

always @ (posedge clock or posedge reset) //always block for the ticker
begin
 if(reset)
  ticker <= 0;
 else if(ticker == 50000000) //reset after 1 second
  ticker <= 0;
 else
  ticker <= ticker + 1;
end

reg [3:0] clickcount; //register to hold the count upto 9. That is why a 4 bit register is used. 3 bit would not have been enough.

assign click = ((ticker == 50000000)?1'b1:1'b0); //click every second

always @ (posedge click or posedge reset)
begin
 if(reset)
  clickcount <= 0;
 else if(clickcount == 8)
   clickcount <= 0;
  else 
  clickcount <= clickcount + 1;

end

always @ (*) //always block that will scroll or move the text. Accomplished with case
begin
    case(clickcount)
    0:
    begin
     fourth = 4; //H
     third = 3; //E
     second = 7; //L
     first = 7; //L
    end
    
    1:
    begin
     fourth = 3; //E
     third = 7; //L
     second = 7; //L
     first = 0; //O
    end
    
    2:
    begin
     fourth = 7; //L
     third = 7; //L
     second = 0; //O
     first = 2; //-
    end
    
    3:
    begin
     fourth = 7; //L
     third = 0; //O
     second = 2; //-
     first = 1; //T
    end
    
    4:
    begin
     fourth = 0; //O
     third = 2; //-
     second = 1; //T
     first = 4; //H
    end
    
    5:
    begin
     fourth = 2; //-
     third = 1; //T
     second = 4; //H
     first = 3; //E
    end
    
    6:
    begin
     fourth = 1; //T
     third = 4; //H
     second = 3; //E
     first = 8; //R
    end
    
    7:
    begin
     fourth = 4; //H
     third = 3; //E
     second = 8; //R
     first = 3; //E
    end
    
    8:
    begin
     fourth = 3; //E
     third = 8; //R
     second = 3; //E
     first = 2; //blank
    end
  endcase
  
end

//see my other post on explanation of LED multiplexing.

localparam N = 18;

reg [N-1:0]count;

always @ (posedge clock or posedge reset)
 begin
  if (reset)
   count <= 0;
  else
   count <= count + 1;
 end

reg [6:0]sseg;
reg [3:0]an_temp;

always @ (*)
 begin
  case(count[N-1:N-2])
   
   2'b00 : 
    begin
     sseg = first;
     an_temp = 4'b1110;
    end
   
   2'b01:
    begin
     sseg = second;
     an_temp = 4'b1101;
    end
   
   2'b10:
    begin
     sseg = third;
     an_temp = 4'b1011;
    end
    
   2'b11:
    begin
     sseg = fourth;
     an_temp = 4'b0111;
    end
  endcase
 end
assign an = an_temp;

reg [6:0] sseg_temp; 
always @ (*)
 begin
  case(sseg)
   4 : sseg_temp = 7'b0001001; //to display H
   3 : sseg_temp = 7'b0000110; //to display E
   7 : sseg_temp = 7'b1000111; //to display L
   0 : sseg_temp = 7'b1000000; //to display O
   1 : sseg_temp = 7'b0000111; //to display T
   8 : sseg_temp = 7'b0001000; //to display R
   
   default : sseg_temp = 7'b1111111; //blank
  endcase
 end
assign {g, f, e, d, c, b, a} = sseg_temp; 
assign dp = 1'b1;

endmodule

Wednesday, 19 December 2012

FIFO(First In First Out) Buffer in Verilog

A FIFO(First in First Out) buffer is an elastic storage usually used between two subsystems. As the name indicates the memory that is first written into the FIFO is the first to be read or processed. A FIFO has two control signals i.e. write and read. When write is enabled data is written into the buffer and when read is enabled data is "removed" from the buffer to make room for more data. This concept of write and read (remove) can be best understood from the conceptual diagram of a FIFO below:

As can be seen, once the data is read, it can be considered as removed and thus allowing more data to be written into the buffer.

Implementation of FIFO in Verilog

To implement FIFO in verilog imagine the memory components to be arranged in a circular queue fashion with two pointers; write and read. The write pointer points to the start of the circle whereas the read pointer points to the end of the circle. Both these pointers increment themselves by one after each read or write operation.
This buffer will also consist of two flags; empty and full. These will help in indicating when the FIFO is full (cannot be written to) or empty (cannot be read from). This circular implementation can be seen from the following figure:


The size of the FIFO buffer greatly depends on the number of address bits. As the number of words in fifo = 2^(number of address bits). The FIFO I will be coding here will consist of 16 memory elements ( 4 bits in each memory element and 3 address bits). This can be easily changed by changing the parameters in the code, by doing so you can create a buffer of any size. The parameters can be changed from the following line:

module fifo # (parameter abits = 4, dbits = 3)

At reset the empty flag is set to high whereas the full flag is set to low to allow new data to be written. The positions of the read and write pointers are also initialized to 0. The verilog code is shown below. It has been commented in detail so I hope each step is clear.

module fifo # (parameter abits = 4, dbits = 3)(
    input clock,
    input reset,
    input wr,
    input rd,
  input [dbits-1:0] din,
    output empty,
    output full,
  output [dbits-1:0] dout
    );

wire db_wr, db_rd;
reg dffw1, dffw2, dffr1, dffr2;
reg [dbits-1:0] out;

always @ (posedge clock) dffw1 <= wr; 
always @ (posedge clock) dffw2 <= dffw1;

assign db_wr = ~dffw1 & dffw2; //monostable multivibrator to detect only one pulse of the button

always @ (posedge clock) dffr1 <= rd;
always @ (posedge clock) dffr2 <= dffr1;

assign db_rd = ~dffr1 & dffr2; //monostable multivibrator to detect only one pulse of the button


reg [dbits-1:0] regarray[2**abits-1:0]; //number of words in fifo = 2^(number of address bits)
reg [abits-1:0] wr_reg, wr_next, wr_succ; //points to the register that needs to be written to
reg [abits-1:0] rd_reg, rd_next, rd_succ; //points to the register that needs to be read from
reg full_reg, empty_reg, full_next, empty_next;

assign wr_en = db_wr & ~full; //only write if write signal is high and fifo is not full

//always block for write operation
always @ (posedge clock)
 begin
  if(wr_en)
   regarray[wr_reg] <= din;  //at wr_reg location of regarray store what is given at din

 end
 
//always block for read operation
always @ (posedge clock)
 begin
  if(db_rd)
   out <= regarray[rd_reg];
 end
 

always @ (posedge clock or posedge reset)
 begin
  if (reset)
   begin
   wr_reg <= 0;
   rd_reg <= 0;
   full_reg <= 1'b0;
   empty_reg <= 1'b1;
   end
  
  else
   begin
   wr_reg <= wr_next; //created the next registers to avoid the error of mixing blocking and non blocking assignment to the same signal
   rd_reg <= rd_next;
   full_reg <= full_next;
   empty_reg <= empty_next;
   end
 end
 
always @(*)
 begin
  wr_succ = wr_reg + 1; //assigned to new value as wr_next cannot be tested for in same always block
  rd_succ = rd_reg + 1; //assigned to new value as rd_next cannot be tested for in same always block
  wr_next = wr_reg;  //defaults state stays the same
  rd_next = rd_reg;  //defaults state stays the same
  full_next = full_reg;  //defaults state stays the same
  empty_next = empty_reg;  //defaults state stays the same
  
   case({db_wr,db_rd})
    //2'b00: do nothing LOL..
    
    2'b01: //read
     begin
      if(~empty) //if fifo is not empty continue
       begin
        rd_next = rd_succ;
        full_next = 1'b0;
       if(rd_succ == wr_reg) //all data has been read
         empty_next = 1'b1;  //its empty again
       end
     end
    
    2'b10: //write
     begin
      
      if(~full) //if fifo is not full continue
       begin
        wr_next = wr_succ;
        empty_next = 1'b0;
        if(wr_succ == (2**abits-1)) //all registers have been written to
         full_next = 1'b1;   //its full now
       end
     end
     
    2'b11: //read and write
     begin
      wr_next = wr_succ;
      rd_next = rd_succ;
     end
     //no empty or full flag will be checked for or asserted in this state since data is being written to and read from together it can  not get full in this state.
    endcase
   

 end

assign full = full_reg;
assign empty = empty_reg;
assign dout = out;
endmodule