Designing a circuit requires several steps: Writing HDL (Verilog) code, compiling the code to produce a circuit, then simulating the circuit and fixing bugs.
设计电路的步骤:
- 写HDL(Verilog)代码;
- 编译代码生成一个电路;
- Altera Quartus
- 逻辑综合,Logic Synthesis
- 模拟电路 Simulation;
- ModelSim
- 修复bug;
Basic
线网(wire): 一输入和一输出的模块(module)
Verilog 内的线网(wire)和其他信号是 有方向的(directional),信息流仅有一个方向,从源到汇(from source to the sinks),source 也经常被称作 driver:驱动一个值到一个wire上。连接组件
模块的端口也有方向(通常为输入和输出),输入端口由模块外部的一些东西驱动,而输出端口驱动外部的一些东西。从模块内部的角度来看,输入端口是一个driver或者源,而输出端口是一个汇
Verilog内 assign 是“持续赋值”,驱动右端信号的值到左端的wire上。
持续赋值的意思是 赋值是一直持续的,即使右端的值改变(左端的值会随着右端的值改变而改变),每当任何一个输入改变,输出被重新计算(recompute)
多个assign语句在代码会的顺序是没有关系的;
assign 语句描述/创建 things 间的连接(connection),不是从一个thing到另一个thing的拷贝值的动作
Vectors
Vector 使用一个名字将相关的信号组合在一起,以更方便操作;例如,wire [7:0] w
,声明了一个8比特的w,功能上等价于8个独立的 wire
在声明的时候,vector 的维度放在变量名的前面,而 part select 的时候将为度放在向量名的后面
wire [99:0] my_vector; // Declare a 100-element vector
assign out = my_vector[10]; // Part-select one bit out of the vector
vector的声明:
type [upper:lower] vector_name;
在 Verilog 里,一旦一个 vector 被声明成一种特定的字节序,它必须总是写相同的字节序
e.g., writing vec[0:3]
when vec
is declared wire [3:0] vec;
is illegal.
注:vector的字节序
小端模式:least significant bit 有 更低的index e.g., [3:0]
大端模式:least significant bit 有 更高的index e.g., [0:3]
Implicit nets
在 Verilog 里,net-type信号可以被 assign
语句隐式地创建; 或者 将为定义的一些东西连接到模块端口上时,会创建隐式地 net-type 信号
隐式 net 总是 1 比特的 wire,如果你本意是想使用 vector 时会产生 bug
使用 default_nettype none
directive可以disable创建隐式net
wire [2:0] a, c; // Two vectors
assign a = 3'b101; // a = 101
assign b = a; // b = 1 implicitly-created wire
assign c = b; // c = 001 <-- bug
my_module i1 (d,e); // d and e are implicitly one-bit wide if not declared. // This could be a bug if the port was intended to be a vector.
Unpacked vs. Packed Arrays
声明的时候vector的索引被写在名字的前面,这些比特被 pack 在一起形成一个 blob;
unpacked 的维度被声明在名字的后面。通常被用于声明memory数组
reg [7:0] mem [255:0]; // 256 unpacked elements, each of which is a 8-bit packed vector of reg.
reg mem2 [28:0]; // 29 unpacked elements, each of which is a 1-bit reg.
访问整个 vector 使用 vector 的名字
assign w = a;
当左右两边的位宽不匹配时,进行0扩展或者截断处理
部分选择操作符
访问vector的一部分
w[3:0] // Only the lower 4 bits of w
x[1] // The lowest bit of x
x[1:1] // ...also the lowest bit of x
z[-1:-2] // Two lowest bits of z
b[3:0] // Illegal. Vector part-select must **match the direction of the declaration.**
b[0:3] // The *upper* 4 bits of b.
assign w[3:0] = b[0:3]; // Assign upper 4 bits of b to lower 4 bits of w. w[3]=b[0], w[2]=b[1], etc.
按位操作符
按位与(&
)、按位或(|
)、按位非(~
)、按位同或(~^
)、按位异或(^
)
按位操作符对 2 个操作数的每 1bit 数据进行按位操作。
如果 2 个操作数位宽不相等,则用 0 向左扩展补充较短的操作数。
取反操作符只有一个操作数,它对操作数的每 1bit 数据进行取反操作。
归约操作符
归约操作符包括:归约与(&
),归约与非(~&
),归约或(|
),归约或非(~|
),归约异或(^
),归约同或(~^
)。
归约操作符只有一个操作数,它对向量操作数逐位进行操作,最终产生一个 1bit 结果。
拼接操作符
通过拼接更小规模的向量得到更大的向量;
{3'b111, 3'b000} => 6'b111000
{1'b1, 1'b0, 3'b101} => 5'b10101
{4'ha, 4'd10} => 8'b10101010 // 4'ha and 4'd10 are both 4'b1010 in binary
注意⚠️:
拼接需要知道每个组件的宽度(否则怎么知道结果的长度?);
因此,{1, 2, 3}
是不合法的,会导致错误信息:unsized constants are not allowed in concatenations
拼接操作符可以被用在 赋值符号 的两边;