Verilog HDL module invocation
We use an example to show how a module to invoke other modules in Verilog HDL. Suppose we want to design a simple ALU as shown in the following figure.
Following is the schematic of the ALU. The top ALU module will invoke two mux2x32 (32-bit 2-to-1 multiplexer) modules and one addsub32 (32-bit adder/subtracter) module.
First, we give the Verilog HDL codes of the mux2x32 and addsub32 modules as follows.
1 2 3 4 5 6 |
module mux2x32 (a0, a1, s, y); input [31:0] a0, a1; // a0, a1: 32-bit input s; // s: 1-bit output [31:0] y; // y: 32-bit assign y = s ? a1 : a0; // like C endmodule |
1 2 3 4 5 6 |
module addsub32 (a, b, sub, s); input [31:0] a, b; input sub; output [31:0] s; assign s = sub ? a - b : a + b; endmodule |
Then, we give the Verilog HDL code of the top ALU module in structural style, as follows. It invokes mux2x32 and addsub32 modules with its own signals as the parameters for the invoked modules. Note that the order of the paramemters is important in such invocation. There is another method to pass the parameters to the invoked modules which does not care about the order. Please investigate it by yourself.
1 2 3 4 5 6 7 8 9 10 11 12 |
module alu1 (a, b, aluc, s); input [31:0] a, b; input [1:0] aluc; output [31:0] s; wire [31:0] d_and = a & b; // AND wire [31:0] d_or = a | b; // OR wire [31:0] d_ao, d_as; // 32-bit wires // use other modules: mux2x32 mx_ao (d_and, d_or, aluc[0], d_ao); addsub32 as (a, b, aluc[0], d_as); mux2x32 mx_la (d_ao, d_as, aluc[1], s); endmodule |
The test bench codes are shown below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
`timescale 1ns/1ns module alu1_tb; reg [31:0] a, b; reg [1:0] aluc; wire [31:0] s; alu1 alu (a, b, aluc, s); initial begin #0 aluc = 2'd0; #0 a = 32'haaaaaaaa; #0 b = 32'hcccccccc; #2 b = 32'h55555555; #2 $stop; end always #1 aluc = aluc + 2'd1; endmodule |
The simulation waveform is shown below. In 0 - 1ns, aluc = 00, a (1010) & c (1100) = 8 (1000); in 1 - 2ns, aluc = 01, a (1010) | c (1100) = e (1110); in 2 - 3ns, aluc = 10, a (1010) + 5 (0101) = f (1111); and in 3 - 4ns, aluc = 11, a (1010) - 5 (0101) = 5 (0101);
The following code also implements the ALU above.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
module alu2 (a, b, aluc, s); input [31:0] a, b; input [1:0] aluc; output [31:0] s; assign s = operate(a, b, aluc); function [31:0] operate; input [31:0] x, y; input [1:0] c; case (c) 2'b00: operate = x & y; 2'b01: operate = x | y; 2'b10: operate = x + y; 2'b11: operate = x - y; endcase endfunction endmodule |
Verilog HDL sequential circuit design
Two sequential circuit models are shown below. The key point of the sequential circuits is that there are components that can record the current state, d flip flop (dff) in the figure, for instance. The other two modules, "Next state" and "Output function" are combinatorial circuits.
D flip flop
A D flip flop (DFF) can store one bit information. In the raising edge (the transition from 0 to 1) of a clock, the input information d is stored. We also need a clear (reset) signal to reset the state of the DFF. According to the relationship between clear and clock, we have two types DFFs: synchronous clear DFF and asynchronous clear DFF. The following figure shows a 32-bit DFF (dff32).
Synchronous clear DFF
1 2 3 4 5 6 7 8 9 10 11 12 13 |
module dff32_sync (clk, clrn, d, q); input [31:0] d; input clk, clrn; output [31:0] q; reg [31:0] q; always @ (posedge clk) begin if (clrn == 0) begin q <= 0; end else begin q <= d; end end endmodule |
Asynchronous clear DFF
1 2 3 4 5 6 7 8 9 10 11 12 13 |
module dff32_async (clk, clrn, d, q); input [31:0] d; input clk, clrn; output [31:0] q; reg [31:0] q; always @ (negedge clrn or posedge clk) begin if (clrn == 0) begin q <= 0; end else begin q <= d; end end endmodule |
D flip flop with enable
DFF stores d on every raising edge of the clock. DFFE adds an enable signal to control the updating.
Synchronous clear DFFE
1 2 3 4 5 6 7 8 9 10 11 12 13 |
module dffe32_sync (clk, clrn, e, d, q); input [31:0] d; input clk, clrn, e; output [31:0] q; reg [31:0] q; always @ (posedge clk) begin if (clrn == 0) begin q <= 0; end else begin if (e) q <= d; end end endmodule |
Asynchronous clear DFFE
1 2 3 4 5 6 7 8 9 10 11 12 13 |
module dffe32_async (clk, clrn, e, d, q); input [31:0] d; input clk, clrn, e; output [31:0] q; reg [31:0] q; always @ (negedge clrn or posedge clk) begin if (clrn == 0) begin q <= 0; end else begin if (e) q <= d; end end endmodule |
A sequential circuit example
A state transition diagram:
Implementation in Verilog HDL:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
module fsm (x,clk,clrn,state); // a finite state example input x,clk,clrn; // x==1: s0,s1,s3,s0,s1,s3,... output [1:0] state; // else: s0,s1,s2,s0,s1,s2,... parameter [1:0] s0 = 2'b00, // define parameters s1 = 2'b01, s2 = 2'b10, s3 = 2'b11; reg [1:0] next_state; // will be nets always @* begin // combinational circuit here case (state) s0: begin next_state = s1; end s1: begin next_state = x? s3 : s2; end default: begin next_state = s0; // default end endcase end reg [1:0] state; // registers for state always @ (posedge clk or negedge clrn) begin if (clrn == 0) begin state <= s0; end else begin state <= next_state; // state transition end end endmodule |
Testbench:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
`timescale 1ns/1ps module fsm_tb; reg x,clk,clrn; wire [1:0] state; fsm inst (x,clk,clrn,state); initial begin #0 clk = 1; clrn = 0; x = 0; #1 clrn = 1; #6 x = 1; #6 x = 0; #7 $stop; end always #1 clk = ~clk; endmodule |
Simulation waveform:
x = 0: state: 0,1,2,0
x = 1: state: 0,1,3,0
Counter16 design in Verilog HDL
A 4-bit counter16:
1 2 3 4 5 6 7 8 9 10 11 12 |
module counter16 (clk, clrn, q); input clk, clrn; output [3:0] q; reg [3:0] q; always @ (negedge clrn or posedge clk) begin if (clrn == 0) begin q <= 0; end else begin q <= q + 4'd1; end end endmodule |
Test bench for the counter:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
`timescale 1ns/1ns module counter16_tb; reg clk, clrn; wire [3:0] q; counter16 cnt (clk, clrn, q); initial begin #0 clrn = 0; #0 clk = 1; #1 clrn = 1; #41 $stop; end always #1 clk = ~clk; endmodule |
Waveform:
Counter10 design in Verilog HDL
A 4-bit counter10:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
module counter10 (clk, clrn, q); input clk, clrn; output [3:0] q; reg [3:0] q; always @ (negedge clrn or posedge clk) begin if (clrn == 0) begin q <= 0; end else begin if (q == 4'd9) q <= 4'd0; else q <= q + 4'd1; end end endmodule |
Test bench for the counter:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
`timescale 1ns/1ns module counter10_tb; reg clk, clrn; wire [3:0] q; counter10 cnt (clk, clrn, q); initial begin #0 clrn = 0; #0 clk = 1; #1 clrn = 1; #41 $stop; end always #1 clk = ~clk; endmodule |
Waveform:
Clock divider
Clock divider generates a new clock whose frequency f1 = f0 / n where f0 is the original clock frequency and n is a constant. The following example shows the case of n = 10. The idea is to prepare a counter with initial value of 0. The counter increments at the raising edge of the input clock. When the counter value reach n / 2 - 1, the output clock inverts and the counter is reset to 0. In the following example, because n = 10 and n / 2 = 5, we have to use a 3-bit counter to count from 0 to 4. That is, $\lceil\log_25\rceil=3$ and 3-bit counter can count 0, 1, 2, 3, 4, 5, 6, and 7.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
module clock10 (clk0, clk1); input clk0; output clk1; reg clk1 = 1; // start state reg [2:0] counter = 0; always @ (posedge clk0) begin if (counter == 3'd4) begin counter <= 0; clk1 = ~clk1; // reverse clk1 end else begin counter <= counter + 3'd1; end end endmodule |
Test bench:
1 2 3 4 5 6 7 8 9 10 11 |
`timescale 1ns/1ns module clock10_tb; reg clk0; wire clk1; clock10 div10 (clk0, clk1); initial begin #0 clk0 = 1; #50 $stop; end always #1 clk0 = ~clk0; endmodule |
Waveform (the frequency of clk1 is 1/10 of the clk0's frequency):
Exercise
Design a 4-bit counter and display the counter value with a 7-segment LED. The frequency of the counter is 1Hz which can be generated from the 50MHz system clock. Project name: counter_7s_led.
The top module counter_7s_led.v is given below. You need to write the codes of two modules: gen_sclk and four_cnt.
1 2 3 4 5 6 7 8 9 10 11 12 |
module counter_7s_led (sys_clk, seg7); input sys_clk; // system clock, 50MHz output [6:0] seg7; // 7-segment LED 0-F wire sec_clk; // second clock, 1Hz // generate sec_clk (1Hz) from sys_clk (50MHz) gen_sclk s1 (sys_clk, sec_clk); // module gen_sclk // display 4-bit counter with 7-segment LED four_cnt s7 (sec_clk, seg7); // module four_cnt endmodule |
gen_sclk.v:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
module gen_sclk (clk, sec_clk); input clk; // 50MHz output reg sec_clk = 0; // second clock, 1Hz. 50MHz / 50M = 1Hz; 50M / 2 = 25M; counter: 0 - 25M-1 reg [24:0] clk_cnt = 25'd4; // 25-bit counter counts 0, 1, 2, ... 24999999 always @ (posedge clk) begin //if (clk_cnt == 25'd4) begin // for simulation if (clk_cnt == 25'd24999999) begin clk_cnt <= 0; sec_clk <= ~sec_clk; end else begin clk_cnt <= clk_cnt + 25'd1; end end endmodule |
four_cnt.v:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
module four_cnt (sec_clk, seg7);
input sec_clk;
output [6:0] seg7;
reg [3:0] cnt = 4'hf;
always @ (posedge sec_clk)
cnt <= cnt + 1;
assign seg7 = func (cnt);
function [6:0] func;
input [3:0] cnt;
case(cnt)
4'h0 : func = 7'b1000000;
4'h1 : func = 7'b1111001;
4'h2 : func = 7'b0100100;
4'h3 : func = 7'b0110000;
4'h4 : func = 7'b0011001;
4'h5 : func = 7'b0010010;
// ... ... for 6, 7, 8, 9, A, b, c, d, E, and F
endcase
endfunction
endmodule
|
Write a test bench counter_7s_led_tb.v to simulate counter_7s_led.v with ModelSim. You can use add wave -r /* to show the waveforms of internal signals.
Implement it on FPGA.