目录
- 预备知识点:
- always块有两种形式,一种是括号里是*,另一种是有posedge和nagedge的,其中前者用于组合逻辑电路,后者用于时序逻辑电路。
-
- 【阻塞赋值 vs 非阻塞赋值】
- verilog中有三种赋值方式:
- 连续赋值(assign x=y;),只能在always块外使用。
- 阻塞赋值(x=y;),只能在always块内使用。
- 非阻塞赋值(x<=y;)只能在always块内使用。
- 在组合逻辑的always块中(always @(*))使用阻塞赋值语句;
- 在时序逻辑的always块中(always @(posedge clk))使用非阻塞赋值语句。
Build a 2-to-1 mux that chooses between a and b. Choose b if both sel_b1 and sel_b2 are true. Otherwise, choose a. Do the same twice, once using assign statements and once using a procedural if statement.
CORRECT:
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
out_always<=a;
end
endmodule
HINT:
如何避免在使用if语句时生成锁存器?
tip:锁存器与触发器的区别?
锁存器是一种对脉冲电平(也就是0或者1)敏感的存储单元电路,而触发器是一种对脉冲边沿(即上升沿或者下降沿)敏感的存储电路。
当我们在设计电路时,不能直接先写成代码然后期望它直接生成为合适的电路,如下典型错误所示:
If (cpu_overheated) then shut_off_computer = 1;
If (~arrived) then keep_driving = ~gas_tank_empty;
语法上正确的代码并不意味着设计成的电路也是合理的。我们来思考这么一个问题,如上图的错误示例,如果if条件不满足,输出如何变化呢?Verilog给出的解决方法是:保持输出不变。因为组合逻辑电路不能记录当前的状态,所以就会综合出锁存器。
所以当我们使用if语句或者case语句时,我们必须考虑到所有情况并给对应情况的输出进行赋值,就意味着我们要为else或者default中的输出赋值。
CORRECT:
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)
shut_off_computer = 1;
else
shut_off_computer=0;
end
always @(*) begin
if (~arrived)
keep_driving = ~gas_tank_empty;
else
keep_driving=0;
end
endmodule
HINT:
这个题有点意思,是让你纠正bug的题。题目的初衷就是让你用if或者case语句的时候一定要把所有情况都列出来,别写得跟“无头厉鬼”似的,让人摸不着头脑。
Case statements are more convenient than if statements if there are a large number of cases. So, in this exercise, create a 6-to-1 multiplexer. When sel is between 0 and 5, choose the corresponding data input. Otherwise, output 0. The data inputs and outputs are all 4 bits wide.
Be careful of inferring latches
ERRO:
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 // This is a combinational circuit
case(sel)
1'd0:out=data0;
1'd1:out=data1;
1'd2:out=data2;
1'd3:out=data3;
1'd4:out=data4;
1'd5:out=data5;
1'd6:out=0;
1'd7:out=0;
default:out=0;
endcase
end
endmodule
CORRECT:
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 // This is a combinational circuit
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=0;
endcase
end
endmodule
HINT:
既然前面都定义是4位的data信号了,就别非得用十进制秀了。。。。
Build a 4-bit priority encoder. For this problem, if none of the input bits are high (i.e., input is zero), output zero. Note that a 4-bit number has 16 possible combinations.
CORRECT1:
module top_module (
input [3:0] in,
output reg [1:0] pos );
always@(*)
begin
case(in)
4'b0000:pos=0;
4'b0001:pos=0;
4'b0010:pos=1;
4'b0011:pos=0;
4'b0100:pos=2;
4'b0101:pos=0;
4'b0110:pos=1;
4'b0111:pos=0;
4'b1000:pos=3;
4'b1001:pos=0;
4'b1010:pos=1;
4'b1011:pos=0;
4'b1100:pos=2;
4'b1101:pos=0;
4'b1110:pos=1;
4'b1111:pos=0;
default:pos=0;
endcase
end
endmodule
CORRECT2:
module top_module (
input [3:0] in,
output reg [1:0] pos );
always @(*) begin
case(1)
in[0]:pos = 0;
in[1]:pos = 1;
in[2]:pos = 2;
in[3]:pos = 3;
default:pos = 0;
endcase
end
endmodule
CORRECT3:
module top_module (
input [3:0] in,
output reg [1:0] pos );
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
endmodule
HINT:
Using hexadecimal (4'hb) or decimal (4'd11) number literals would save typing vs. binary (4'b1011) literals.
优先编码器是一种组合电路,当给定输入位向量时,输出该向量中第一个1的位置。 例如,给定输入8’b10010000的8位优先级编码器将输出3’d4,因为bit [4]是高的第一位。
构建一个4位优先级编码器。 对于此问题,如果所有输入位都不为高(即输入为零),则输出零。 请注意,一个4位数字具有16种可能的组合。
CORRECT1不好,因为它不适用情况多的项目;其他两种比较简洁方便。
假设构建一个电路来处理游戏的PS/2键盘上的扫描代码。对于收到的最后两个字节的扫描码,我们需要指示是否按下了键盘上的一个方向键。这涉及到一个相当简单的映射,它可以实现为一个case语句(或if-elseif),包含四个case。
电路有一个16位输入和四个输出。建立能识别这四种扫描码并正确输出的电路。
ERRO:
module top_module (
input [15:0] scancode,
output reg left,
output reg down,
output reg right,
output reg up );
always @(*)
begin
case(scancode)
16'he06b:left=1;
16'he072:down=1;
16'he074:right=1;
16'he075:up=1;
default:left=0;
default:down=0;
default:right=0;
default:up=0;
endcase
end
endmodule
CORRECT:
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
HINT:
case语句中的default不能有多个只能有一个,但是为避免生成锁存器,所有的输入情况必须要被考虑到。但仅有一个简单的default是不够的,我们必须在case item和default中为4个输出进行赋值,这会导致很多不必要的代码编写。
一种简单的方式就是对输出先进行赋初值的操作,这种类型的代码确保在所有可能的情况下输出都被赋值,除非case语句覆盖了赋值。这也意味着不再需要缺省的default项。