刚接触Verilog的时候,说实话我完全懵了。这玩意儿跟平时写的C语言、Python完全不是一个套路!!!后来才明白,Verilog不是用来写软件的,它是用来描述硬件电路的。这就好比一个是画图纸,一个是搭积木,本质完全不同。
Verilog HDL(Hardware Description Language)是一种硬件描述语言。简单来说,就是用代码的方式来描述数字电路的行为和结构。
想象一下,如果你要设计一个复杂的芯片,用传统的电路图来画会画到手软。但是用Verilog,你可以用类似编程的方式来描述这个芯片应该怎么工作。然后通过综合工具,这些代码就能自动转换成实际的电路!
现在各大科技公司都在疯狂招聘会Verilog的工程师。从手机芯片到AI加速器,从网络设备到汽车电子,到处都需要这个技能。
在Verilog中,所有的设计都是以模块为基本单位的。这就像搭积木一样,每个模块都是一个小积木块。
verilog module simple_gate( input wire a, input wire b, output wire y ); assign y = a & b; endmodule
这个例子就是一个简单的与门。module关键字开始,endmodule结束(超级重要!!!忘记写endmodule会报错到你怀疑人生)。
Verilog里面主要有两种数据类型:
wire(线网类型) - 就像物理世界的导线,不能存储数据 - 适合用来连接模块之间的信号 - 一般用于组合逻辑
reg(寄存器类型) - 可以存储数据,就像内存一样 - 主要用在时序逻辑中 - 注意!!!这里的reg不一定对应物理寄存器,只是个数据类型而已
verilog wire [7:0] data_bus; // 8位宽的线网 reg [15:0] counter; // 16位的寄存器 reg enable; // 1位的寄存器
Verilog的运算符跟C语言挺像的,但也有自己的特色:
```verilog // 逻辑运算 assign result1 = a && b; // 逻辑与 assign result2 = a || b; // 逻辑或 assign result3 = !a; // 逻辑非
// 位运算 assign result4 = a & b; // 按位与 assign result5 = a | b; // 按位或 assign result6 = a ^ b; // 按位异或 assign result7 = ~a; // 按位取反
// 算术运算 assign sum = a + b; // 加法 assign diff = a - b; // 减法 ```
刚开始的时候经常搞混逻辑运算和位运算,这个真的需要多练习。
组合逻辑就是输出只依赖当前输入的电路。就像一个函数,给定输入就能立即得到输出。
verilog module mux_2to1( input wire sel, input wire [7:0] in0, input wire [7:0] in1, output wire [7:0] out ); assign out = sel ? in1 : in0; endmodule
这是个2选1的多路选择器。当sel为1时选择in1,为0时选择in0。三目运算符在Verilog里面特别常用!
verilog module decoder_3to8( input wire [2:0] addr, output reg [7:0] dout ); always @(*) begin case(addr) 3'b000: dout = 8'b00000001; 3'b001: dout = 8'b00000010; 3'b010: dout = 8'b00000100; 3'b011: dout = 8'b00001000; 3'b100: dout = 8'b00010000; 3'b101: dout = 8'b00100000; 3'b110: dout = 8'b01000000; 3'b111: dout = 8'b10000000; default: dout = 8'b00000000; endcase end endmodule
这里的@(*)表示对所有信号敏感,也就是任何输入变化都会触发这个always块。case语句跟C语言的switch很像,但记得加default(不然综合工具会不高兴)。
时序逻辑就是有记忆功能的电路,输出不仅依赖当前输入,还依赖之前的状态。
verilog module d_flip_flop( input wire clk, input wire rst_n, input wire d, output reg q ); always @(posedge clk or negedge rst_n) begin if(!rst_n) q <= 1'b0; else q <= d; end endmodule
这是个D触发器的例子。posedge clk表示时钟上升沿,negedge rst_n表示复位信号下降沿。
注意这里用的是<=而不是=!!!在时序逻辑中一定要用非阻塞赋值(<=),这是个大坑,踩过的人都懂。
verilog module counter_8bit( input wire clk, input wire rst_n, input wire enable, output reg [7:0] count ); always @(posedge clk or negedge rst_n) begin if(!rst_n) count <= 8'b0; else if(enable) count <= count + 1'b1; end endmodule
一个简单的8位计数器。当enable信号有效时,每个时钟周期计数值加1。
这个概念真的是初学者的噩梦!我当时也被搞得晕头转向。
阻塞赋值(=) - 立即执行,就像C语言的赋值 - 主要用于组合逻辑 - 在always块中按顺序执行
非阻塞赋值(<=) - 在时钟边沿统一更新 - 主要用于时序逻辑 - 所有赋值并行执行
```verilog // 错误的写法!!! always @(posedge clk) begin a = b; // 阻塞赋值 c = a; // 这里的a已经是新值了 end
// 正确的写法 always @(posedge clk) begin a <= b; // 非阻塞赋值 c <= a; // 这里的a还是旧值 end ```
记住这个规则:组合逻辑用=,时序逻辑用<=。违反这个规则会导致仿真和综合结果不一致,那就完蛋了。
写好单个模块后,就可以把它们组合起来构建更复杂的系统了。
```verilog module top_module( input wire clk, input wire rst_n, input wire [7:0] data_in, output wire [7:0] data_out ); wire [7:0] counter_out; wire enable_sig;
endmodule ```
这就是模块的实例化,就像搭积木一样把各个小模块组合起来。
写完代码不能就完事了,必须要仿真验证!这就像软件开发中的测试一样重要。
```verilog module tb_counter(); reg clk; reg rst_n; reg enable; wire [7:0] count;
endmodule ```
testbench就是专门用来测试的模块,不需要综合成硬件。通过给被测模块施加各种激励,观察其行为是否符合预期。
```verilog module traffic_light( input wire clk, input wire rst_n, output reg [1:0] light ); parameter RED = 2'b00; parameter YELLOW = 2'b01; parameter GREEN = 2'b10;
endmodule ```
状态机是数字设计中的核心概念,用来实现复杂的控制逻辑。
刚开始学的时候,建议先从简单的组合逻辑开始,比如加法器、多路选择器这些。然后再学时序逻辑,掌握触发器、计数器的设计。
接下来可以尝试设计一些小项目,比如数字时钟、简单的CPU、通信接口等。这些项目能让你把理论知识串联起来,真正理解硬件设计的思维方式。
千万不要急于求成!!!硬件设计跟软件编程的思维差别很大,需要时间来适应。多看别人的代码,多动手实践,慢慢就能找到感觉了。
最后提醒一点,学Verilog的同时也要了解一些数字电路的基础知识,这样才能更好地理解为什么要这样设计。毕竟,Verilog只是工具,真正重要的是掌握数字系统设计的思想和方法。
加油吧,硬件设计的世界虽然复杂,但也充满了乐趣!当你第一次成功点亮LED、第一次让自己设计的CPU跑起来程序的时候,那种成就感是无法用语言来形容的。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。