层次化设计
层次化设计是数字电路设计的重要方法,通过模块实例化和参数化设计实现代码复用和层次化管理。
模块实例化
基本实例化
// 子模块定义
module and_gate(
input a,
input b,
output y
);
assign y = a & b;
endmodule
// 父模块实例化
module top_module(
input a,
input b,
output y
);
// 实例化子模块
and_gate u_and(
.a(a),
.b(b),
.y(y)
);
endmodule
端口连接方式
命名端口连接(推荐)
module_name instance_name (
.port1(signal1),
.port2(signal2),
.port3(signal3)
);
优点:
- 端口顺序无关
- 可读性好
- 易于维护
位置端口连接
module_name instance_name (signal1, signal2, signal3);
缺点:
- 必须按照定义顺序
- 可读性差
- 容易出错
端口位宽匹配
module adder(
input [7:0] a,
input [7:0] b,
output [7:0] sum
);
assign sum = a + b;
endmodule
module top;
wire [15:0] data1, data2;
wire [15:0] result;
// 位宽不匹配警告
adder u_adder(
.a(data1[7:0]), // 正确:选择低 8 位
.b(data2[7:0]),
.sum(result[7:0])
);
endmodule
未连接端口
module counter(
input clk,
input reset,
input enable,
output [7:0] count,
output carry
);
module top;
wire clk, reset;
wire [7:0] count;
counter u_counter(
.clk(clk),
.reset(reset),
.enable(1'b1), // 常量连接
.count(count),
.carry() // 未连接(悬空)
);
endmodule
参数化设计
参数定义
module counter #(
parameter WIDTH = 8,
parameter MAX_COUNT = 255
)(
input clk,
input reset,
output [WIDTH-1:0] count
);
reg [WIDTH-1:0] counter;
always @(posedge clk or posedge reset) begin
if (reset)
counter <= 0;
else if (counter == MAX_COUNT)
counter <= 0;
else
counter <= counter + 1;
end
assign count = counter;
endmodule
参数覆盖
实例化时覆盖
// 位置参数覆盖
counter #(16, 65535) u_counter_16bit (
.clk(clk),
.reset(reset),
.count(count)
);
// 命名参数覆盖(推荐)
counter #(
.WIDTH(16),
.MAX_COUNT(65535)
) u_counter_16bit (
.clk(clk),
.reset(reset),
.count(count)
);
defparam 覆盖(不推荐)
counter u_counter(clk, reset, count);
defparam u_counter.WIDTH = 16;
defparam u_counter.MAX_COUNT = 65535;
参数传递
module top #(
parameter DATA_WIDTH = 8
)(
input clk,
input reset,
input [DATA_WIDTH-1:0] data_in,
output [DATA_WIDTH-1:0] data_out
);
// 将参数传递给子模块
register #(
.WIDTH(DATA_WIDTH)
) u_reg (
.clk(clk),
.reset(reset),
.enable(1'b1),
.din(data_in),
.dout(data_out)
);
endmodule
localparam
localparam 不能被外部覆盖:
module fifo #(
parameter WIDTH = 8,
parameter DEPTH = 16
)(
input clk,
input reset,
// ...
);
// 从其他参数计算得出
localparam ADDR_WIDTH = $clog2(DEPTH);
localparam DEPTH_MINUS_1 = DEPTH - 1;
reg [ADDR_WIDTH-1:0] wr_ptr, rd_ptr;
// ...
endmodule
生成语句
generate for
module adder_array #(
parameter WIDTH = 8,
parameter COUNT = 4
)(
input [WIDTH*COUNT-1:0] a,
input [WIDTH*COUNT-1:0] b,
output [WIDTH*COUNT-1:0] sum
);
genvar i;
generate
for (i = 0; i < COUNT; i = i + 1) begin : adder_gen
wire [WIDTH-1:0] a_slice = a[WIDTH*i +: WIDTH];
wire [WIDTH-1:0] b_slice = b[WIDTH*i +: WIDTH];
wire [WIDTH-1:0] sum_slice;
adder #(.WIDTH(WIDTH)) u_adder(
.a(a_slice),
.b(b_slice),
.sum(sum_slice)
);
assign sum[WIDTH*i +: WIDTH] = sum_slice;
end
endgenerate
endmodule
generate if
module configurable_module #(
parameter IMPLEMENTATION = "FAST"
)(
input clk,
input reset,
input [7:0] data_in,
output [7:0] data_out
);
generate
if (IMPLEMENTATION == "FAST") begin : fast_impl
// 快速实现(面积大)
pipeline_register u_reg(
.clk(clk),
.reset(reset),
.din(data_in),
.dout(data_out)
);
end else begin : small_impl
// 小面积实现(速度慢)
simple_register u_reg(
.clk(clk),
.reset(reset),
.din(data_in),
.dout(data_out)
);
end
endgenerate
endmodule
generate case
module alu #(
parameter WIDTH = 8,
parameter ALU_TYPE = "BASIC"
)(
input [WIDTH-1:0] a,
input [WIDTH-1:0] b,
input [3:0] op,
output [WIDTH-1:0] result
);
generate
case (ALU_TYPE)
"BASIC": begin : basic_alu
basic_alu u_alu(.a(a), .b(b), .op(op), .result(result));
end
"EXTENDED": begin : extended_alu
extended_alu u_alu(.a(a), .b(b), .op(op), .result(result));
end
default: begin : default_alu
basic_alu u_alu(.a(a), .b(b), .op(op), .result(result));
end
endcase
endgenerate
endmodule
层次结构访问
层次路径
module top;
wire [7:0] data;
submodule u_sub(
.data(data)
);
// 访问子模块内部信号
initial begin
$display("submodule signal: %h", top.u_sub.internal_signal);
end
endmodule
module submodule(
output [7:0] data
);
reg [7:0] internal_signal;
// ...
endmodule
层次化名称
module top;
// 使用层次化名称访问
initial begin
$monitor("Counter value: %d", top.u_counter.counter);
end
counter u_counter(.clk(clk), .reset(reset), .count(count));
endmodule
设计实例
多位加法器
module adder_nbit #(
parameter N = 4
)(
input [N-1:0] a,
input [N-1:0] b,
input cin,
output [N-1:0] sum,
output cout
);
wire [N:0] carry;
assign carry[0] = cin;
assign cout = carry[N];
genvar i;
generate
for (i = 0; i < N; i = i + 1) begin : full_adder_gen
full_adder u_fa(
.a(a[i]),
.b(b[i]),
.cin(carry[i]),
.sum(sum[i]),
.cout(carry[i+1])
);
end
endgenerate
endmodule
module full_adder(
input a,
input b,
input cin,
output sum,
output cout
);
assign sum = a ^ b ^ cin;
assign cout = (a & b) | (b & cin) | (a & cin);
endmodule
参数化寄存器堆
module register_file #(
parameter DATA_WIDTH = 32,
parameter ADDR_WIDTH = 5,
parameter NUM_REGS = 32
)(
input clk,
input wen,
input [ADDR_WIDTH-1:0] waddr,
input [DATA_WIDTH-1:0] wdata,
input [ADDR_WIDTH-1:0] raddr1,
input [ADDR_WIDTH-1:0] raddr2,
output [DATA_WIDTH-1:0] rdata1,
output [DATA_WIDTH-1:0] rdata2
);
reg [DATA_WIDTH-1:0] regs [0:NUM_REGS-1];
// 写端口
always @(posedge clk) begin
if (wen)
regs[waddr] <= wdata;
end
// 读端口
assign rdata1 = regs[raddr1];
assign rdata2 = regs[raddr2];
endmodule
参数化多路选择器
module mux_n_to_1 #(
parameter WIDTH = 8,
parameter NUM_INPUTS = 4
)(
input [WIDTH-1:0] inputs [0:NUM_INPUTS-1],
input [$clog2(NUM_INPUTS)-1:0] sel,
output [WIDTH-1:0] y
);
assign y = inputs[sel];
endmodule
// 使用示例
module top;
wire [7:0] data [0:3];
wire [1:0] sel;
wire [7:0] out;
mux_n_to_1 #(
.WIDTH(8),
.NUM_INPUTS(4)
) u_mux(
.inputs(data),
.sel(sel),
.y(out)
);
endmodule
参数化 FIFO
module sync_fifo #(
parameter DATA_WIDTH = 8,
parameter DEPTH = 16
)(
input clk,
input reset,
input wr_en,
input rd_en,
input [DATA_WIDTH-1:0] din,
output [DATA_WIDTH-1:0] dout,
output full,
output empty
);
localparam ADDR_WIDTH = $clog2(DEPTH);
reg [DATA_WIDTH-1:0] mem [0:DEPTH-1];
reg [ADDR_WIDTH:0] wr_ptr;
reg [ADDR_WIDTH:0] rd_ptr;
// 写操作
always @(posedge clk) begin
if (wr_en && !full)
mem[wr_ptr[ADDR_WIDTH-1:0]] <= din;
end
// 读操作
assign dout = mem[rd_ptr[ADDR_WIDTH-1:0]];
// 指针管理
always @(posedge clk or posedge reset) begin
if (reset) begin
wr_ptr <= 0;
rd_ptr <= 0;
end else begin
if (wr_en && !full)
wr_ptr <= wr_ptr + 1;
if (rd_en && !empty)
rd_ptr <= rd_ptr + 1;
end
end
// 状态信号
assign full = (wr_ptr[ADDR_WIDTH] != rd_ptr[ADDR_WIDTH]) &&
(wr_ptr[ADDR_WIDTH-1:0] == rd_ptr[ADDR_WIDTH-1:0]);
assign empty = (wr_ptr == rd_ptr);
endmodule
设计规范
命名约定
// 模块名:小写字母,下划线分隔
module adder_8bit;
// 实例名:u_ 前缀
adder_8bit u_adder_0;
// 参数:大写字母,下划线分隔
parameter DATA_WIDTH = 8;
// 信号:小写字母,下划线分隔
wire [DATA_WIDTH-1:0] data_in;
wire [DATA_WIDTH-1:0] data_out;
// 时钟和复位
wire clk;
wire reset_n; // _n 表示低有效
// 寄存器:_reg 后缀
reg [DATA_WIDTH-1:0] counter_reg;
// 状态:_state 后缀
reg [2:0] current_state;
reg [2:0] next_state;
文件组织
project/
├── rtl/
│ ├── top.v
│ ├── submodule1.v
│ └── submodule2.v
├── tb/
│ ├── tb_top.v
│ └── testdata/
├── constraints/
│ └── top.xdc
└── sim/
└── run_sim.sh
模块结构
//============================================================================
// Module Name: module_name
// Description: 模块描述
// Author: 作者
// Date: 日期
//============================================================================
module module_name #(
// 参数定义
parameter PARAM1 = 8,
parameter PARAM2 = 16
)(
// 端口定义
input clk,
input reset,
input [PARAM1-1:0] data_in,
output [PARAM1-1:0] data_out
);
// 内部信号
wire [PARAM1-1:0] internal_signal;
// 子模块实例化
submodule #(
.WIDTH(PARAM1)
) u_submodule (
.clk(clk),
.reset(reset),
.data_in(data_in),
.data_out(internal_signal)
);
// 功能实现
assign data_out = internal_signal;
endmodule
小结
本章介绍了 Verilog 层次化设计:
- 模块实例化:命名端口连接、位置端口连接
- 参数化设计:parameter、localparam、参数覆盖
- 生成语句:generate for、generate if、generate case
- 层次结构访问:层次路径、层次化名称
- 设计实例:多位加法器、寄存器堆、多路选择器、FIFO
- 设计规范:命名约定、文件组织、模块结构
层次化设计是构建复杂数字系统的基础。下一章将介绍测试平台编写。