文章目录
1.Always block(combinational)
有两种类型的always块:
组合:always@(*)
时序:always@(posedge clk)
组合always块等效于assign语句
例如下面描述了相同的电路
assign out1 = a & b | c ^ d;
always@(*) out2 = a & b | c ^ d;
module top_module(
input a,
input b,
output wire out_assign,
output reg out_alwaysblock);
assign out_assign = a & b;
always@(*)begin
out_alwaysblock = a & b;
end
endmodule
2.Always block(clocked)
verilog中有三种赋值方式:
1.连续赋值:assign x = y;只能在always块外使用
2.阻塞赋值:x = y;只能在always块内使用
3.非阻塞赋值:x <= y;只能在always块内使用
在组合逻辑always块中(always @(*))使用阻塞赋值语句;
在时序逻辑always块中(always@(posedge clk)) 使用非阻塞赋值语句;
使用赋值语句,组合always块和时序always块三种方式构建一个异或门。
module top_module(
input clk,
input a,
input b,
output wire out_assign,
output reg out_always_comb,
output reg out_always_ff);
assign out_assign = a ^ b;
always@(*)begin
out_always_comb = a ^ b;
end
always@(posedge clk)begin
out_always_ff <= a ^ b;
end
endmodule
3.If statement
一个if语句会产生一个2选1的数据选择器,相当于三元运算符
always@(*)begin
if(condition)begin
out = x;
end
else begin
out = y;
end
end
assign out = (condition)?x : y;
建立一个2比1的mux,在a和b之间进行选择。如果sel_b1和sel_b2都为真,选择b。重复两次,一次使用assign语句,另一次使用过程性if语句
module top_module(
input a,
input b,
input sel_b1,
input sel_b2,
output wire out_assign,
output reg out_always);
assign out_assign = (sel_b1 & sel_b2) ? b : a;
always@(*)begin
if(sel_b1 & sel_b2)begin
out_always = b;
end
else begin
out_always = a;
end
end
endmodule
4.If statement latches
锁存器与触发器的区别?
锁存器是一种对脉冲电平(也就是0或1)敏感的存储单元电路,而触发器是一种对脉冲(即上升沿或者下降沿)敏感的存储电路。
当我们在使用if语句或者case语句时,我们必须考虑到所有情况并给对应情况的输出进行赋值,意味着我们要为else或者default中的输出赋值。
module top_module(
input cpu_overheated,
output reg shut_off_computer,
input arrived,
input gas_tank_empty,
output reg keep_driving);
always@(*)begin
if(cpu_overheated)begin
shut_off_computer = 1;
end
else begin
shut_off_computer = 0;
end
end
always@(*)begin
if(~arrived)begin
keep_driving = ~gas_tank_empty;
end
else begin
keep_driving = 0;
end
end
endmodule
5.Case statement
Verilog中的case语句几乎等同于if-else-if-else序列,它的语法和功能不同于C语言中的switch语句
always @(*) begin // This is a combinational circuit
case (in)
1'b1: begin
out = 1'b1; // begin-end if >1 statement
end
1'b0: out = 1'b0;
default: out = 1'bx;
endcase
end
1.case语句以case开始,每个case的选项以分号结束。
2.每个case的选项中只能执行一个statement,所以就无需break语句。但如果我们想在一个case选项中执行多个statement,就需要使用begin…end
3.case中可以有重复的case item,首次匹配的将会被执行
Case语句在Case数量较多的情况下比if语句更方便。所以,在这个练习中,创建一个6比1的多路复用器。当sel在0 ~ 5之间时,选择相应的数据输入。否则,输出0。数据输入和输出都是4位宽。
module top_module (
input [2:0] sel,
input [3:0] data0,
input [3:0] data1,
input [3:0] data2,
input [3:0] data3,
input [3:0] data4,
input [3:0] data5,
output reg [3:0] out );
always@(*) begin
case(sel)
3'b000: out = data0;
3'b001: out = data1;
3'b010: out = data2;
3'b011: out = data3;
3'b100: out = data4;
3'b101: out = data5;
default: out = 3'b0;
endcase
end
endmodule
6.Priority encoder
优先编码器是一种组合电路,当给定输入位向量时,输出向量中前1位的位置。例如,给定输入8’b10010000的8位优先编码器将输出3’d4,因为位[4]是第一个高的位。
module top_module (
input [3:0] in,
output reg [1:0] pos );
always@(*) begin
case(in)
4'b0000:pos = 2'b00;
4'b0001:pos = 2'b00;
4'b0010:pos = 2'b01;
4'b0011:pos = 2'b00;
4'b0100:pos = 2'b10;
4'b0101:pos = 2'b00;
4'b0110:pos = 2'b01;
4'b0111:pos = 2'b00;
4'b1000:pos = 2'b11;
4'b1001:pos = 2'b00;
4'b1010:pos = 2'b01;
4'b1011:pos = 2'b00;
4'b1100:pos = 2'b10;
4'b1101:pos = 2'b00;
4'b1110:pos = 2'b01;
4'b1111:pos = 2'b00;
default:pos = 2'b00;
endcase
end
endmodule
7.Priority encoder with casez
casez的作用,在比较中,它将值z的位视为不关心
always @(*) begin
casez (in[3:0])
4'bzzz1: out = 0; // in[3:1] can be anything
4'bzz1z: out = 1;
4'bz1zz: out = 2;
4'b1zzz: out = 3;
default: out = 0;
endcase
end
module top_module(
input [7:0] in,
output reg [2:0] pos);
always@(*)begin
casez(in)
8'bzzzzzzz1:pos = 0;
8'bzzzzzz10:pos = 1;
8'bzzzzz100:pos = 2;
8'bzzzz1000:pos = 3;
8'bzzz10000:pos = 4;
8'bzz100000:pos = 5;
8'bz1000000:pos = 6;
8'b10000000:pos = 7;
default: pos = 0;
endcase
end
endmodule
8.Avoiding latches
为避免生成锁存器,所有的输入情况必须要被考虑到。但仅有一个简单的default是不够的,我们必须在case item和default中为4个输出进行赋值,这会导致很多不必要的代码编写。
一种简单的方式就是对输出先进行赋初值的操作,这种类型的代码确保在所有可能的情况下输出都被赋值,除非case语句覆盖了赋值。
module top_module (
input [15:0] scancode,
output reg left,
output reg down,
output reg right,
output reg up );
always @(*) begin
left=0;down=0;right=0;up=0;
case(scancode)
16'he06b:left=1;
16'he072:down=1;
16'he074:right=1;
16'he075:up=1;
endcase
end
endmodule