Hello Verilog: Coding
Today, we will be using Verilog, a HDL (hardware description language) to code an encoder to light up a seven segment display based on keymap input.
First of all, create a file name hello_verilog.v
in a directory and open it with a text editor of your choice.
To code a module in Verilog, one needs to specify the start and end of it via the module
and endmodule
keywords:
module MODULE_NAME (WIRE1, WIRE2, WIRE3);
// Module content
endmodule
Note that a semicolon needs to follow the declaration of a module, rather than a curly bracket {
in C or colon :
in Python.
On the same line of the module declartion, one should specify both the input and output wire names, which will be connected internally in the module.
After declaring the module, we percede to specify the output and input wires:
input [20:0] pb;
output [7:0] ss0
Here the keywords input
and output
indicate whether the wire should be considered as input wire or output wire, and the square bracket indicate the bit width of the wire. [20:0]
means that pb
is a 21 bits wire or 21 bits vector. In our example, pb
should be a keymap and ss0
should be connected to the seven segment display.
For this introduction, we will be using a binary priority encoder to map the input to the corresponding bits combination in order to light up the seven segment display.
// Binary Priority Encoder
wire [4:0] encoded;
assign encoded = pb[0] == 1 ? 4'h0 :
pb[1] == 1 ? 4'h1 :
pb[2] == 1 ? 4'h2 :
pb[3] == 1 ? 4'h3 :
pb[4] == 1 ? 4'h4 :
pb[5] == 1 ? 4'h5 :
pb[6] == 1 ? 4'h6 :
pb[7] == 1 ? 4'h7 :
pb[8] == 1 ? 4'h8 :
pb[9] == 1 ? 4'h9 :
pb[10] == 1 ? 4'ha :
pb[11] == 1 ? 4'hb :
pb[12] == 1 ? 4'hc :
pb[13] == 1 ? 4'hd :
pb[14] == 1 ? 4'he :
pb[15] == 1 ? 4'hf :
5'b10000;
In this block of code, we first specify an internal 5 bit wire to hold the keymap input. We then uses the cascaded ternary ops EXPRESSION ? VAL_TRUE : VAL_FALSE
to assign the corresponding encoded value.
The term 4'hX
means that this is a literal, with 4 bit in width and express in hexdecimal format (the h
). We can also specify literal in other form: 5'b10000
means a 5 bits binary literal with the MSB as 1 and the rest as 0.
After creating an encoder, we then build up a lookup table for the seven segment display to output the correct bit value.
// Seven Segment Lookup table
wire [7:0] SEG [16:0];
This line of code, much like the previous, specify an internal 8 bits wire. However, notice the brackets behind the wire name [16:0]
, this indicates that the wire contains a total number of 17 channels of 8 bits wire. It is like creating a 2 dimension array in C. We will use these 17 channels of 8 bits wire to construct a lookup table as follow:
assign SEG[5'b00000] = char0;
assign SEG[4'b0001] = char1;
assign SEG[4'b0010] = char2;
assign SEG[4'b0011] = char3;
assign SEG[4'b0100] = char4;
assign SEG[4'b0101] = char5;
assign SEG[4'b0110] = char6;
assign SEG[4'b0111] = char7;
assign SEG[4'b1000] = char8;
assign SEG[4'b1001] = char9;
assign SEG[4'b1010] = charA;
assign SEG[4'b1011] = charB;
assign SEG[4'b1100] = charC;
assign SEG[4'b1101] = charD;
assign SEG[4'b1110] = charE;
assign SEG[4'hf] = charF;
assign SEG[5'b10000] = blank;
assign ss0 = SEG[encoded];
Much like in C or Python, we put the index in the square bracket and use the keyword assign
to connect the wire to other literals. Here the variables charX
are localparams which store the corresponding bit values of the segment display.
Finally, we assign the output wire ss0
to the correct segment value.
Right now, we have created the desired Verilog code to perform the function we like. However, we will need a way to run and test it in the simulation environment prior to implement as ASIC or FPGA. These parts will be covered in the article: Hello Verilog: Simulation.
The complete code is as follow:
module hello (ss0, pb);
input [20:0] pb;
output [7:0] ss0;
// Define constants
localparam blank = 8'b00000000;
localparam char0 = 8'b00111111;
localparam char1 = 8'b00000110;
localparam char2 = 8'b01011011;
localparam char3 = 8'b01001111;
localparam char4 = 8'b01100110;
localparam char5 = 8'b01101101;
localparam char6 = 8'b01111101;
localparam char7 = 8'b00000111;
localparam char8 = 8'b01111111;
localparam char9 = 8'b01101111;
localparam charA = 8'b01110111;
localparam charB = 8'b01111100;
localparam charC = 8'b00111001;
localparam charD = 8'b01011110;
localparam charE = 8'b01111001;
localparam charF = 8'b01110001;
localparam charG = 8'b01101111;
localparam charH = 8'b01110110;
localparam charI = 8'b00000100;
localparam charJ = 8'b00001110;
localparam charL = 8'b00111000;
localparam charN = 8'b01010100;
localparam charO = 8'b01011100;
localparam charP = 8'b01110011;
localparam charR = 8'b01010000;
localparam charS = 8'b01101101;
localparam charU = 8'b00111110;
localparam charY = 8'b01101110;
// Encoder
wire [4:0] encoded;
assign encoded = pb[0] == 1 ? 4'h0 :
pb[1] == 1 ? 4'h1 :
pb[2] == 1 ? 4'h2 :
pb[3] == 1 ? 4'h3 :
pb[4] == 1 ? 4'h4 :
pb[5] == 1 ? 4'h5 :
pb[6] == 1 ? 4'h6 :
pb[7] == 1 ? 4'h7 :
pb[8] == 1 ? 4'h8 :
pb[9] == 1 ? 4'h9 :
pb[10] == 1 ? 4'ha :
pb[11] == 1 ? 4'hb :
pb[12] == 1 ? 4'hc :
pb[13] == 1 ? 4'hd :
pb[14] == 1 ? 4'he :
pb[15] == 1 ? 4'hf :
5'b10000;
// Seven Segment Lookup table
wire [7:0] SEG [16:0];
assign SEG[5'b00000] = char0;
assign SEG[4'b0001] = char1;
assign SEG[4'b0010] = char2;
assign SEG[4'b0011] = char3;
assign SEG[4'b0100] = char4;
assign SEG[4'b0101] = char5;
assign SEG[4'b0110] = char6;
assign SEG[4'b0111] = char7;
assign SEG[4'b1000] = char8;
assign SEG[4'b1001] = char9;
assign SEG[4'b1010] = charA;
assign SEG[4'b1011] = charB;
assign SEG[4'b1100] = charC;
assign SEG[4'b1101] = charD;
assign SEG[4'b1110] = charE;
assign SEG[4'hf] = charF;
assign SEG[5'b10000] = blank;
assign ss0 = SEG[encoded];
endmodule