参考链接:HDLBits导学
Problem 28: Always blocks(combinational) (Alwaysblock1)
问题:使用assign语句和组合always块来构建与门
解决:
// synthesis verilog_input_version verilog_2001
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
Problem 29: Always blocks(clocked) (Alwaysblock2)
概述:在组合always块中,使用阻塞性赋值。在时序always块中,使用非阻塞性赋值。具体为什么对设计硬件用处不大,还需要理解Verilog模拟器如何跟踪事件
问题:使用assign语句,组合always块和时序always块这三种方式来构建异或门。 请注意,时钟always块生成了与另外两个不同的电路,多了一个触发器,因此输出会有一定的延迟。
解决:
// synthesis verilog_input_version verilog_2001
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
结果:
Problem 30: If statement(Always if)
问题:构建一个可以在a和b之间选择的二选一多路复用器。如果sel_b1和sel_b2都为真,输出b,其他情况输出a。请使用两种方法作答,一次使用assign赋值,一次使用if语句。
解决:
// synthesis verilog_input_version verilog_2001
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
注意:要对sel_b1和sel_b2的组合进行判断才是正确的
Problem 31: If statement latches(Always if2)
问题:示例:以下代码包含生成锁存器的错误,请勿模仿!!!修复错误,只有当它真的过热时才关闭计算机,真的到达目的地或者需要加油时,才停止驾驶。
always @(*) begin
if (cpu_overheated)
shut_off_computer = 1;
end
always @(*) begin
if (~arrived)
keep_driving = ~gas_tank_empty;
end
思路:主要理解一下题目的意思
解决:
// synthesis verilog_input_version verilog_2001
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 = ~arrived;
end
endmodule
注意:只写if的话,输出保持不变,这就意味着电路需要记住当前状态,从而产生锁存器。组合逻辑(比如逻辑门)不能记住任何状态
Problem 32: Casestatement(Always case)
问题:如果存在大量的case项,则case语句比if语句更方便。 因此,在本练习中,创建一个6选1的多路复用器。当sel介于0和5之间时,选择相应的数据输入。 其他情况输出0。数据输入和输出均为4位宽。
注意:不要生成锁存器(意味着sel都要有对应的值,默认值也行)
解决:
// synthesis verilog_input_version verilog_2001
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'd0:out = data0;
3'd1:out = data1;
3'd2:out = data2;
3'd3:out = data3;
3'd4:out = data4;
3'd5:out = data5;
default:out = 3'd0;
endcase
end
endmodule
注意:3'd5不是代表5需要3位的十进制表示,3位指的是2进制的位数,前面的数字指示的都是二进制的位数,而d5只是单纯将b101表示为d5。还有就是记得使用default,如果你不需要记忆电路前一个状态的值得话
Problem 33: Priority encoder(Always case2)
概述:优先编码器是组合电路,当给定输入时,输出输入向量中的右边第一个1的位置。例如,输入8'b10010000的,则优先编码器将输出3'd4,因为位[4]是从右数第一个1。
问题:构建一个4位优先编码器。如果没有输入均为零,则输出零。请注意,4位数字有16种输入发的可能。
小提示:使用十六进制(4'hb)或十进制(4'd11)与二进制(4'b1011)相比可以节省打字量。
解决:
// synthesis verilog_input_version verilog_2001
module top_module (
input [3:0] in,
output reg [1:0] pos );
always @(*) begin
case(in)
4'b0000:pos = 2'd0;
4'b0001:pos = 2'd0;
4'b0010:pos = 2'd1;
4'b0011:pos = 2'd0;
4'b0100:pos = 2'd2;
4'b0101:pos = 2'd0;
4'b0110:pos = 2'd1;
4'b0111:pos = 2'd0;
4'b1000:pos = 2'd3;
4'b1001:pos = 2'd0;
4'b1010:pos = 2'd1;
4'b1011:pos = 2'd0;
4'b1100:pos = 2'd2;
4'b1101:pos = 2'd0;
4'b1110:pos = 2'd1;
4'b1111:pos = 2'd0;
endcase
end
endmodule
注意:个人感觉in在使用的时候选择二进制比较好,看起来比较方便
Problem 34: Priority encoder with casez(Always casez)
概述:如果还按上一个练习(Problem 33: Priority encoder(Always case2))写case语句的话,case语句中将有256个case项。如果case语句中的case项与某些输入无关,就可以减少列出的case项(在本题中减少到9个)。这就是casez的用途:它在比较中将具有值z的位视为无关项(即输入01都会匹配到)。
例如:下面的代码就是上一个联系中的4输入优先编码器:
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
问题:构建一个8输入的优先编码器。给定一个8位向量,输出输入向量中左数第一个1的位置。如果输入均为0,则输出零
解决:
// synthesis verilog_input_version verilog_2001
module top_module (
input [7:0] in,
output reg [2:0] pos );
always @(*) begin
casez(in)
8'bzzzzzzz1: pos = 3'd0;
8'bzzzzzz1z: pos = 3'd1;
8'bzzzzz1zz: pos = 3'd2;
8'bzzzz1zzz: pos = 3'd3;
8'bzzz1zzzz: pos = 3'd4;
8'bzz1zzzzz: pos = 3'd5;
8'bz1zzzzzz: pos = 3'd6;
8'b1zzzzzzz: pos = 3'd7;
default:pos = 3'd0;
endcase
end
endmodule
注意:是casez不是case!!!
Problem 35: Always nolatches(Always nolatches)
问题:假设您再写一个来处理用于游戏PS/2键盘扫描码的电路。给出接收到扫描码的最后的两个字节,您需要判断是否有按键被按下。这是一个相当简单的映射,可以使用case语句或者if-else语句实现,一共有如下四种情况。
概述:同时为避免生成了不必要的锁存器,必须在所有条件下为所有的输出赋值。这可能会多打很多字,使你的代码变得冗长。 一个简单的方法是在case语句之前为输出分配一个“默认值”:
always @(*) begin
up = 1'b0; down = 1'b0; left = 1'b0; right = 1'b0;
case (scancode)
... // Set to 1 as necessary.
endcase
end
除非case语句覆盖赋值,否则这种代码样式可确保在所有可能的情况下输出0。 这也意味着case的default项变得不必要。
解决:
// synthesis verilog_input_version verilog_2001
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