【CS基础学习笔记-1】数字电路和硬件语言
芯片前端 101
一般情况下我们接触到的芯片已经不是传统意义上的CPU了,我们称现在集成度很高的芯片为“片上系统”(SoC:System-on-a-chip)。以一款手机的SoC举例:SoC上集成了CPU核、高速缓存、GPU单元、NPU单元、通讯基带、ISP、LPDDR5内存、存储控制器等等模块。
数字电路处理的是量信号,用来实现特定的逻辑功能,更容易被抽象形成一种专门的描述语言。现在业界的主要标准语言是VHDL和Verilog HDL。
在使用硬件描述语言的时候,我们需要理解每条语句对应的电路,从电路的角度思考它为何这样设计。比如:
-
声明一个 reg,那么这个变量就有寄存数值的功能,后面可以综合出一个实际的寄存器
-
指定一段 wire,那么它就只能传递数据
-
编写一个判断语句,可能就对应了一个 MUX 数据选择器
-
编写一个 for 循环可能就是把一段电路重复多次...
-
always 可以指定某个信号在某个值或者某个跳变时,执行块里的代码
芯片设计前端做的事就是:将电路用代码表示出来,由计算机将代码转换为逻辑电路
芯片设计规划
模块拆分 -> 功能模块拆分 -> 实例化子模块... 最终形成一个 hierarchy 结构。简单来说就是自顶向下。Verilog 都是基于模块编写的,大部分Verilog逻辑语句都放在模块内部
代码实例 -- 4位十进制计数器
module counter(
//端口定义
input reset_n, //复位端,低有效
input clk, //输入时钟
output [3:0] cnt, //计数输出
output cout //溢出位
);
reg [3:0] cnt_r ; //计数器寄存器
always@(posedge clk or negedge reset_n) begin
if(!reset_n) begin //复位时,计时归0
cnt_r <= 4'b0000 ;
end
else if (cnt_r==4'd9) begin //计时10个cycle时,计时归0
cnt_r <=4'b0000;
end
else begin
cnt_r <= cnt_r + 1'b1 ; //计时加1
end
end
assign cout = (cnt_r==4'd9) ; //输出周期位
assign cnt = cnt_r ; //输出实时计时器
endmodule
Module 模块结构
module counter(
// 接口部分
input reset_n,
input clk,
output [3:0] cnt,
output cout
);
…… // 逻辑功能部分
endmodule
Verilog模块的接口必须指定它是输入还是输出:输入信号使用input声明,输出信号使用output声明。此外,还有一种双向信号,使用inout声明。
数据类型
reg [3:0] cnt_r;
定义了位宽为 4bit 的寄存器reg类型信号。寄存器reg类型表示抽象数据单元,对应一种寄存器电路,reg默认的初始值为X(不确定值),也就是说reg电路在上电后输出高电平还是低电平是不确定的。一般在系统复位信号(reset_n)有效时,给它赋一个确定的值。
reg类型只能在always和initial语句中被赋值。
-
描述语句为时序逻辑 - always语句中带有时钟信号:寄存器变量对应为触发器电路
-
描述语句是组合逻辑 - always语句中不带时钟信号:寄存器变量对应为锁存器电路
wire 类型表示结构实体(例如各种逻辑门)之间的物理连线。
-
驱动线网类型变量的有逻辑门、连续赋值语句、assign 等
-
如果没有驱动元件连接到线网上,线网就默认为高阻态“Z”
Verilog 还定义了一种参数类型,通过 parameter 来声明一个标识符,用来代表一个常量参数,我们称之为符号常量,即标识符形式的常量。这个常量,实际上就是电路中一串由高低电平排列组成的一个固定数值。
数值表达
cnt_r <=4'b0000;
给寄存器赋值,位宽为4,b代表二进制,十进制d,十六进制h。
值 | 解释 |
---|---|
逻辑0 | 低电平,对应电路接地“GND |
逻辑1 | 高电平,对应电路接电源VCC |
逻辑X | 电平未知,输入端存在多种输入情况 |
逻辑Z | 高阻态,外部无激励信号,是一个悬空状态 |
条件、分支、循环语句
条件语句
-
if 对应的条件语句是多级串联的MUX电路
-
case 对应的语句是一个多输入的MUX电路
循环语句
while、repeat、for、forever:只能在always块或者initial块里面使用
过程结构
过程结构是实现时序电路的关键。我们可以利用 alway 块语句设定一个时钟沿,用来触发相应逻辑电路的执行。这样,我们就可以依据时钟周期来分析电路中各个信号的逻辑跳变。而 initial 语句常在验证代码中使用,它可以从仿真的 0 时刻开始设置相关信号的值,并将这些值传输到待验证模块的端口上。
initial 语句。它从 0 时刻开始执行,且内部逻辑语句只按顺序执行一次,多个 initial 块之间是相互独立的。理论上,initial 语句是不可以综合成实际电路的,多用于初始化、信号检测等,也就是在编写验证代码时使用。
扩展阅读:中科大课程手册