You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
113 lines
5.4 KiB
Verilog
113 lines
5.4 KiB
Verilog
module led( // 该括号内对 io口 以及类型 进行说明
|
|
input key, // 默认为 wire 型
|
|
output reg led // 因为需要再always中改变状态, 需要设置为寄存器型
|
|
);
|
|
|
|
parameter WIDTH = 5; // 定义一个内部常量, 可以供模块式用, 当然你这个变量可以在外部被修改, 编译的时候使用, 默认情况下会采用从高位到低位的顺序
|
|
parameter [4:0]a_1 = 4'b1010; // 默认的顺序, 注意, a_1 和a_2 内部的值是完全相同, 只是内部的布局不一样
|
|
|
|
wire [WIDTH:0] tmp1, tmp2; // 定义两个 线类型信号, 宽度为 常量 WIDTH
|
|
|
|
reg [WIDTH:0]v_reg; // 定义一个 寄存器类型, 宽度为 6bit 寄存器类型在下一次触发机制到来之前, 保留原值
|
|
wire [WIDTH:0]v_wire; // 定义一个 线类型, 宽度为 6bit
|
|
|
|
always @(*) begin // * 表示内部的任何变量发生变化的时候, 就会并行执行该 block, 但是排除左值(可能会发生震荡?)
|
|
// 该块 没有带时钟信号, 虽然内部产生的信号定义还是 reg型的, 但是他并没有产生时序逻辑, 还是组合逻辑, 只有带时钟信号之后, 才会使用真正的存储单元/寄存器
|
|
// begin ... end 表示改语句是一个块, 块内的语句是一体的, 如果没有begin ... end, 则只会执行 always 下面紧挨着的 1行!
|
|
led = !key; // 对reg 类型变量 阻塞赋值, 他执行完毕, 才会执行下面的
|
|
|
|
v_reg[1] = key;
|
|
// v_wire[1] = key; // 不能 对 wire 类型 进行赋值, wire类型是用来连接各种门的信号线, 他只能使用 assign进行连线
|
|
end
|
|
|
|
// assign v_reg[0] = key; // 寄存器类型 不能进行 连线, 他只能在 always中进行赋值
|
|
assign v_wire[0] = key;
|
|
assign v_wire[1] = v_reg[1]; // 把 v_wire 线中 下标1的位置直接连到 v_reg寄存器中的下标1
|
|
|
|
|
|
|
|
// 信号的位宽
|
|
// 信号宽度默认是 1bit, 可以省略不写, 注意 位宽不是 数的位数这通常在非二进制数中容易造成混淆
|
|
parameter w_1 = 4'd4; // 这个是 4位宽, 而不是由4位数, 这个变量实际也是 0'b0100
|
|
wire c;
|
|
assign c = 1'b1 + 1'b1; // c的宽度是 1bit, 这个加法溢出了, 溢出的丢失, 所以c 是 1'b0
|
|
|
|
wire f;
|
|
assign f = 1 + 1; // 这里的 1没有些宽度, 默认是32位宽, 但是wire 默认是1位宽, 所以 溢出的全部舍去 f为 1'b10
|
|
|
|
wire [2:0]g;
|
|
assign g = 1'b1 + 1'b1; // 位宽受到左值的影响, 这里 1'b1 + 1'b1 虽然溢出了, 但是却能保存下来, 此时g的值是 2'b10
|
|
|
|
wire a;
|
|
wire b;
|
|
wire d;
|
|
wire s;
|
|
assign a = 1'b1;
|
|
assign b = 1'b1;
|
|
assign {s, d} = a + b; // 也会溢出, 我们把进位保存起来到 s
|
|
|
|
wire [1:0]ret;
|
|
assign {ret[1], ret[0]} = a + b; // 把结果和进位 拼接到位宽为 2 的 ret中
|
|
|
|
|
|
|
|
// 逻辑运算
|
|
wire [2:0]l_a;
|
|
wire [2:0]l_b;
|
|
wire l_c;
|
|
assign l_c = l_a && l_b; // l_a会被认为是一个整体, 这段代码会生成 (l_a[0] || l_a[0] || l_a[0]) && (l_ab0] || l_b[0] || l_b[0]) 也就是按位或之后再与, l_a || l_b 与之相同的逻辑 不过是 按位或之后, 再或
|
|
|
|
// 按位逻辑运算符
|
|
// 单目的按位与, 被与数的左侧 没有操作数
|
|
wire [4:0] W, V;
|
|
assign V = &W; // 这里只是单纯的与W, 注意这里是把 W每位进行相与, 其实就是 V = W[3] & W[2] & W[1] & W[0], 如果 A等于 0b0110, 那么C 就等于 4'b0;
|
|
|
|
// 双目按位与
|
|
wire [4:0]U;
|
|
assign U = W & V; // 连个操作数 按位相与 赋值给U, 如果某个操作数宽度不够, 则对宽度小的操作数在左侧补0
|
|
|
|
|
|
// 逻辑运算符 && || ! 只有1或者0 两个结果(比特位中只要有一个1, 那么该值就是1, 比如 3'b010 && 3'b100 两边都是1), 而 | & ^ ~ 则可以对多位进行操作得到多位的结果
|
|
|
|
|
|
// 比较运算符
|
|
// 比较的时候如果操作数其中有一位是x或者z, 那么结果就是未知 x
|
|
|
|
|
|
// 移位运算, 移位的操作是补零
|
|
// 移位操作是不消耗资源的, 他只是线的连接, 不过如果移位宽度是一个变量而不是常量, 那么综合出来可能会使用多路选择器来控制选择移多少位
|
|
wire [4:0]Y1;
|
|
wire [4:0]Y2;
|
|
assign Y1 = 4'b0110;
|
|
assign Y2 = Y1 << 2; // 移位运算符 也要受到 位宽的约束, 这里Y2位宽是4, 所以Y1的值左移两位 传给 Y2, Y2此时的值是 1000, 内部其实就是把Y2[3:2] 直接连到 Y1上的最低两位[1:0]
|
|
|
|
|
|
// 切片/选择语句, a可以是一个可以变的数, 但是b 必须是一个常数
|
|
// [a-: b] 表示获取 从 a开始获取比a下标小的数, 一共获取 b位
|
|
// [a+: b] 表示获取 比a下标大的数, 一共获取 b位
|
|
wire [16:0] Slice;
|
|
// Slice[6-:3] 等同于 Slice[6:4]
|
|
// Slice[2+:3] 等同于 Slice[4:2]
|
|
|
|
|
|
// 拼接运算符
|
|
// 拼接运算符右值必须要写位宽!! {A[0], 5} 后面的5没有写位宽是禁止的 因为默认是32位或者不知道多少位拼接在一起了
|
|
|
|
|
|
|
|
// 时序逻辑电路设计中, 避免使用信号的上升沿下降沿当作时钟, 这是不稳定的信号
|
|
wire my_s;
|
|
always @(posedge my_s) begin // 尽量不要使用我们自己的信号当作时钟
|
|
|
|
end
|
|
|
|
|
|
// 阻塞赋值和非阻塞
|
|
// 在组合逻辑电路中, 尽量使用阻塞赋值, 在时序逻辑电路中是用非阻塞
|
|
// 比如下面如果 a_1 是1, b_1是0, 我们想把a_1的值先给b_1, 然后再相加起来给c_1, 我们期望的是2, 虽然语法上不报错, 但是再硬件层 是没办法实现的,因为加法器直接连线到 c_1了
|
|
reg a_1,b_1,c_1;
|
|
always @(*) begin
|
|
b_1 <= a_1;
|
|
c_1 <= b_1 + a_1;
|
|
end
|
|
endmodule |