Verilog first contacts
Now we're going to see some samples in order to get familiar with verilog language.
Module
The module in Verilog is the "black box" that defines a circuit that will be created, it defined the ports of our circuit. And the direction of each port. A file can have multiple Verilog modules defined by other sub-modules.
module componente (i1,i2,i3,o); input i1,i2,i3; output o; endmodule
In the code we can define the direction of the ports "i1, i2, i3" as input and "o" as output in Verilog ports may be of the following types:
- input
- output
- inout
- true (1)
- false (0)
- high impedance (Z)
- don't care (X)
Another important point is that our circuit might have a port that is actually a "bus" signal, that is a multiple-bit port size, for example we set the input i1 to 6 bits in size.
module componente (i1,i2,i3,o); input [5:0] i1; // Define the size in bits of sinal i1 (That's a comment) input i2,i3; output o; endmodule
Numbers Syntax
Numbers in verilog are in the following format: size'typevalue
- 2'b10 (binary value "10" 2 bits)
- 'b10 (at least 32 bits binary value)
- 8'hAf (8 bits hexadecimal)
Declaration of "wire" and variables ("reg") We use the "wire" signal any time that we want to pass a value to/from the circuit port. Actually a port is a form of "wire", you can change the wire value using the "assing" statement. Now for declaring variables we use the "reg" statement, variables retain it's value if they are not changed in some situation. Variables can be used only in the procedural region of code. (Within a behavioral description).
module component (i2,o); input i2; wire a; output o; assign a = i2; assign o = a; endmodule
Hardware description at gate level
Now we will learn how to describe our circuits at the gate level. Verilog provides all common ports like and, or, xor, etc... The basic syntax for them is: boolean_function(output,input_1,input_2,...input_n) Bellow we show a basic sample of a hardware described at the gate level.
module combinacional (a,b,c,y); input a,b,c; output y; // Observe that is not necessary to declare the wires "im1,im2,im3" and(im1,a,b); and(im2,b,c); and(im3,a,c); or(y,im1,im2,im3); endmodule
As you can see this is not much different from a digital schematic ...Description at digital equations levelA greater level of abstraction is to use boolean equations to describe circuits
module logic_equation (a,b,c,y); input a,b,c; output y; // Logic equation assign y = (a & b) | (b & c) | (a & c); endmodule
Here is a table of common operators
| Bit operations | &(and) | |(or) | ^(xor) | ~(not) | ~^(xnor) | ^~ |
|---|---|---|---|---|---|---|
| Redution operators | & | ~& | | | ~| | ^ | ~^ |
| Arithmetic operators | + | - | * | / | % | |
| Logical Operators | && | || | ! | |||
| Shift operators | < | > | <= | >= | == | |
| Shift operators | >> | >> | ||||
| Concatenation operators | {} | {n{}} |
Bits concatenation
We can create a bus from a group of signals using the "{}" operator, in the example bellow we have a 4 bit adder, that use this operator to create an output bus.
module adder_4 (a,b,ci,sum,co); input [3:0] a,b; input ci; output [3:0] sum; output co; // {co,soma} Form a 9 bit bus where "co" is the most significant bit assign {co,sum} = a + b + ci; endmodule
"?" Operator
When assigning values to "wire" signals we can use the "?" operator to assing values based in some condition like C. Bellow we got an multiplexer to show how this works.
module mux_2_1 (i0,i1,sel,out); input [3:0] i0,i1,sel; output [3:0] out; // Output will be assigned to i0 if "sel=1" else i1 will be assigned assign out = sel ? i0 : i1; endmodule
The syntax for "?" operator:
assign some_wire = condition ? value_true : value_false;
Concurrency
As we said earlier a different point in HDL languages is that things can run in parallel, for instance, every assing operation will be executed if any signal from the right side of the statement change it's value, no matter the order that it appears in the source code.
module comp_4 (a,b,equal); input [3:0] a,b; output equal; wire [3:0] intermediate; // This line will be executed if a or b change assign intermediate = a ^ b; // This line will be executed if "intermediate" change assign equal = ~| intermediate; endmodule
Behavioral description
Now we will learn how to describe circuits using statements(if,while,for,case)looking more like a normal computer language like python or C, also the coding will be sequential, rather than paralel like the last topic. In order to unleash that we need the "always" statement that defines a procedural area of code.
always @ (a or b or c) begin // Sequential (Procedural area of code) end
This means that always that a,b or c changes, the code between begin..end will be executed sequentially, after it's execution hangs and wait for another event in the sensivity list(a,b,c). In the behavior description the verilog synthesizer will try to find the best "hardware" that implements your algorithm described.Let's describe in the behavioral level the same circuit described earlier with gate level description.
module comportamental(a,b,c,y); input a,b,c; output y; // When we create a reg variable with the same port name we indicate that this port will be altered in the procedural code. reg y; // If some event occur in (a,b,c) the code will be executed always @ (a or b or c) begin y = (a & b) | (b & c) | (a & c); end endmodule
One point to be noted is that we create a "reg" variable with the same name as the output port y, this is done to indicate that the port will be assigned inside the procedural code.Now let's see a multiplex sample using the "if-else" statement.
module mux_behav(i0,i1,sel,out); input i0,i1,sel; output out; reg out; always @ (i0 or i1 or sel) begin if (sel == 1'b0) out = i0; else out = i1; end endmodule
Now a ALU sample using the case statement, the case statement is very useful when we have a lot if if conditionals.
module alu_comportamental(a,b,operacao,saida); input a,b,operacao; output saida; reg saida; always @ (a or b or operacao) begin case (operacao) 2'b00 : saida = a + b; // (Sum) 2'b01 : saida = a - b; // (Subtraction) 2'b10 : saida = a & b; // (AND) 2'b11 : saida = a ^ b; // (XOR) default : saida = 4'b0000; endcase end endmodule
Sequential Circuits
Until now we only see combinational circuits, ie digital circuits that does not have memory, and the response is based only on the current inputs, now, another type of digital circuits will be covered... Circuits with memory, with response based on the inputs and a previous state,those circuits are called Sequential circuits. Samples of sequential circuits are the flip-flops, registers, RAM memory, etc...Normally sequential circuits are described in behavioral mode, and uses some form of clock for syncronization, let's see one of the most simpler samples, the flip-flop D. This circuit gets the input value (D) when the clock signal is rising. In other cases the value is stored in memory.
module FFD(D,CLK,Q,NQ); input D,CLK; output Q,NQ; reg Q,NQ; // This block will be executed when we get the rising edge of CLK signal always @ (posedge CLK) begin Q = D; NQ = ~D; end endmodule
A new statement is the "posedge" that indicates in the sensivity list to trigger in the rising edge of the signal. We can also use the "negedge" to trigger in the falling edge of the signal.By default a variable will retain it's value when no assign is made. So the variables Q and NQ will hold it's values while a posedge doesn't come.Now let's add a asynchronous RESET signal
module FFD(D,CLK,RESET,Q,NQ); input D,CLK; output Q,NQ; reg Q,NQ; // This block will be executed in the clocks rising edge or any change in reset always @ (posedge CLK or RESET) begin if (RESET) begin Q = 1'b0; NQ = 1'b1; end else begin Q = D; NQ = ~D; end end endmodule
Before continuing to the next topic let's describe a general 8 bits register with synchronous reset
module register(d,clk,set,reset,q); input set,reset,clk; input [7:0] d; output [7:0] q; reg [7:0] q; always @ (posedge clk) begin if (set) q = 1'b1; else if (reset) q = 1'b0; else q = d; end endmodule
Finite State machines
State machine is a general sequential circuit composed of (states, transitions and actions) The current state is based on the past states of the system and/or some input. A transition indicates a state change and is described by a condition that would need to be fulfilled to enable the transition. An action is a description of an activity that is to be performed at a given moment. For example if we are on state "opened" and the close_door condition is true we go to the "closed" state and the first action that we make is the "close door" action. There are two types of State machines
- Moore (Output depends only on the state)
- Mealy (Output depends on the state and the input)
Mealy description
This will show how to describe a mealy state machine, first we will present the state machine diagram. This state machine will detect the 101 binary value.
module MealyMachine(x,clk,z); input x,clk; output z; // Define some constant values for reset,peg_1,peg_2 like C enums parameter [1:0] reset = 0, // 0 = 00 peg_1 = 1, // 1 = 01 peg_10= 2; // 2 = 10 reg [1:0] est_corrente; // This block will execute in CLK rising edge always @ (posedge clk) begin case (est_corrente) reset: if (x == 1'b1) est_corrente = peg_1; else est_corrente = reset; peg_1: if (x == 1'b0) est_corrente = peg_10; else est_corrente = peg_1; peg_10:if (x == 1'b1) est_corrente = peg_1; else est_corrente = reset; default: est_corrente = reset; endcase; end // This will run in parallel, observe that the output depends on the input and the state assign z = (est_corrente == peg_10 && x ==1'b1) ? 1'b1 : 1'b0; endmodule
Moore machine description
A Moore has outputs dependent on the current state, a good example is a counter, in this sample we will show a simple 2 bit counter, wich response should look like this....
module count_moore(RESET, CLK, Z); input RESET; input CLK; output [3:0] Z;
reg [3:0] Z; reg [1:0] state;
parameter zero = 0, one = 1, two = 2, three = 3;
// Output depends only on current state always @ (state) begin case (state) zero: Z = 4'b0000; one: Z = 4'b0001; two: Z = 4'b0010; three: Z = 4'b0011; default: Z = 4'b0000; endcase end
always @(posedge CLK or posedge RESET) begin if (RESET) state = zero; else case (state) zero: state = one; one: state = two; two: state = three; three: state = zero; endcase endendmodule