实验练习
说明
- 每一次实验我们会为大家准备若干练习题目。不同的题目有着不同的难度,请大家量力而行;
- 你可以查阅任何开源资料完成实验练习,但不能直接抄袭。一经发现,我们将取消所有抄袭参与者本次的实验成绩。情节特别严重的,我们将按照学校的有关规定进行处理。
- 部分题目我们提供了代码框架,你需要在框架指定的位置完成代码内容。你也可以自由修改框架代码,但需要根据题目要求完成练习。
Verilog 扣分细则
在 Lab1 的实验练习开始之前,我们额外补充一些需要注意的内容。我愿称之为『扣分细则』。
什么?为什么会有扣分细则?这是因为 Verilog 开发中的问题有 90% 以上源于代码编写不规范。为了保证大家后续实验的正常进行,我们在这里明确一些『雷区』,希望大家能够在编程过程中加以注意。
下面的内容是被禁止的:
不要使用基于位置的端口关联
在进行模块例化时,无论端口数目多少,请一定使用基于端口名称的关联。
这是一个典型的反例:
MyModule mymodule(clk, rst, signal1, signal2, signal3, signal4, signal5, signal6, signal7, still_signal, tons_if_signals, i, just, want, to, write, something, here, to_, show, you, how, annoying, the, location_based, correspondence, is, signals_again, bu, la, bu_, la_, bu__, la__, bu___, la___, bu____, la____, ok, that_s, enough);
不要混用阻塞赋值与非阻塞赋值
阻塞赋值 =
只用在 assign
和 always @(*)
的组合逻辑中,非阻塞赋值 <=
只用在 always @(posedge clk)
的时序逻辑中。请不要在错误的场景下使用,甚至二者混用。
这是一个典型的反例:
assign a <= 1;
always @(posedge clk)
a = a + 1;
always @(*) begin
a <= a + 1;
if (b)
a = 0;
end
不要在设计文件中使用 for 循环
Verilog 的 for 循环与 C 语言不一样,因此请不要在设计文件中使用 for 循环。
下面的内容是我们鼓励的:
一个文件里只声明一个 module
这是一个很无理的要求,因为有些模块只有不到 10 行!
事实上,这个规范并不是针对大家本学期的数电实验,而是针对大家下学期组成原理的实验。如果我们把 module 单独写一个文件,我们就可以在 Vivado 中通过点击直接进入这个文件,提升开发效率。另外,如果我们在一个文件中写多个模块,很容易导致找 bug 时在不同模块之间切换时出现错误。所以,建议大家还是分文件写模块吧!
良好的缩进与对齐习惯
为了增强代码可读性,我们鼓励大家养成良好的缩进与对齐习惯。
-
begin
/end
以及括号中的代码要进行一个 Tab 的缩进;end
与开启begin...end
行的字符左对齐;case 语句对每种 case 冒号前的部分进行对齐。例如:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
// 括号中的代码需要缩进 cache_memory way0 ( .addra (w_index), // Tab 缩进 .clka (clk), .dina (mem_din), .ena (mem_en[0]), .wea (mem_we), .addrb (r_index), .doutb (mem_dout0) ); // 分号不要忘记 // begin...end 中的代码需要缩进 always @(*) begin case (wrt_data_sel) // Tab 缩进 1'b0: mem_din = w_data_AXI; 1'b1: begin case(wrt_type) // Tab 缩进 BYTE: mem_din = {64{w_data_CPU[7:0]}}; // Tab 缩进 HALF: mem_din = {32{w_data_CPU[15:0]}}; WORD: mem_din = {16{w_data_CPU}}; default: mem_din = 0; endcase // 与 17 行的 case 对齐 end // 与 1'b1 对齐,因为这个 end 对应 16 行的 begin endcase // 与 14 行的 case 对齐 end // 与 always 对齐
-
在双目运算符的两侧填加空格,如:
assign hit[3] = (tag == tag_3) && vld_3; // 赋值运算符、等于运算符以及逻辑与运算符
-
模块声明的部分至少要和最长的
output
对齐,短于它的要在右边补空格。这样写的好处是,使用多点编辑操作同一列时非常方便(直接 ctrl+alr+↑ 或 ctrl+alr+↓ 即可)。例如:module Check_Data_SEG_SEL ( input [31:0] check_data_if, input [31:0] check_data_id, input [31:0] check_data_ex, input [31:0] check_data_mem, input [31:0] check_data_wb, input [31:0] check_data_hzd, input [2:0] check_addr, output reg [31:0] check_data );
这里实际上添加了额外的空格。当然,你也可以将位宽部分也对齐,例如下面这样:
module Check_Data_SEG_SEL ( input [31:0] check_data_if, input [31:0] check_data_id, input [31:0] check_data_ex, input [31:0] check_data_mem, input [31:0] check_data_wb, input [31:0] check_data_hzd, input [ 2:0] check_addr, output reg [31:0] check_data );
有意义的变量命名
除了要求的最外层的接口名称,内部名称应该具有意义,不要怕名字长,如:
assign index = read_address[11:6]; // 你也可以简写为 r_addr,但不要只写一个 r
必做内容
每一名同学都需要完成必做部分的内容。
题目 1:向量翻转(2 分)
本题来自于 Verilog OJ ID-16。你可以在该平台上验证自己的设计。
请编写 Verilog 代码,将 8bit 的输入信号按位翻转,并输出到输出端口。该过程如下图所示:
我们已经为你准备了可用的框架代码:
module top_module(
input [7:0] in,
output [7:0] out
);
// Your codes should start from here.
// ......
// End of your codes.
endmodule
题目 2:最大值问题(3 分)
下面的 Verilog 代码片段实现了两个数取最大值的功能:
1 2 3 4 5 6 7 8 9 10 11 |
|
请据此完成下面的问题。
-
请使用
assign
语句重新完成该模块的功能。代码框架我们已经为你写好:module MAX2 ( input [7:0] num1, num2, output [7:0] max ); // Your codes should start from here. // ...... // End of your codes. endmodule
-
现在我们需要获得三个数的最大值。请参考
MAX2
的代码,使用always
和if-else
语句完成该功能。代码框架我们已经为你写好:module MAX3 ( input [7:0] num1, num2, num3, output reg [7:0] max ); // Your codes should start from here. // ...... // End of your codes. endmodule
-
现在我们需要获得三个数的最大值。请在下面的框架中通过例化
MAX2
模块实现该功能。module MAX3 ( input [7:0] num1, num2, num3, output [7:0] max ); // Your codes should start from here. // ...... // End of your codes. endmodule
题目 3:1 的个数(3 分)
给定一个位宽为 3 的信号 in
,编写 Verilog 代码以输出其中为 1 的位的数目。例如:如果 in = 3'b011,则模块输出 2'd2;如果 in = 3'b111,则模块输出 2'd3。
我们已经为你准备了可用的框架代码:
module Count4Ones(
input [2:0] in,
output reg [1:0] out
);
// Your codes should start from here.
// ......
// End of your codes.
endmodule
选择性必做内容
选择性必做内容是针对不同层次学生设计的分层内容。不同难度的题目对应不同的分值,请大家根据自身实际情况进行选择。
请在下面的题目中任选一题完成。多选按选择的第一题计分。
题目 1:Logisim 使用(2 分)
1.请根据 Logisim 使用教程中的内容,使用 Logisim 搭建一个 1bit 宽度的二选一选择器并封装。你可以参考我们在 Lab1 实验教程中介绍的电路结构进行设计。
2.请使用上面封装的电路搭建一个 4bits 宽度的二选一选择器。
题目 2:Verilog 运算符(2 分)
阅读以下 Verilog 代码,写出当 a = 8’b0011_0011, b = 8’b1111_0000 时各输出信号的值。
module test(
input [7:0] a, b,
output [7:0] c, d, e, f, g, h, i, j, k, l
);
assign c = a & b;
assign d = a || b;
assign e = a ^ b;
assign f = ~a;
assign g = {a[2:0], b[3:0], {1'b1}};
assign h = b >>> 3;
assign i = &b;
assign j = (a > b) ? a : b;
assign k = a - b;
assign l = !a;
endmodule
选做内容
选做内容为扩展内容,不计入实验成绩。大家可以根据兴趣自行完成。
题目 1:Verilog 数组
Verilog 支持对于数组的声明。请自行查阅有关资料,编写 Verilog 代码,声明一个由 16 个位宽为 32 的 reg 型变量组成的数组。
题目 2:带参数例化
除了我们正文中介绍的使用 #
在例化时传递参数,Verilog 还允许使用 defparam
关键字进行参数设定。请自行查阅有关资料,简述这两种方法的不同之处以及使用场景。