getting started(入门)
getting started(入门)
第一步
欢迎来到HDLBits!
开始数字逻辑设计可能会很难,因为您需要同时学习新的概念、新的硬件描述语言(例如Verilog)、几个新的软件包,通常还有一个FPGA板。HDLBits提供了一种通过单击“模拟”来练习设计和调试简单电路的方法。
设计电路需要几个步骤:编写HDL(Verilog)代码,编译代码以生成电路,然后模拟电路并修复错误。
编写代码
编写代码的最简单方法是在下面的代码编辑器框中完成。对于这个问题,我们已经为您填写了大部分代码。继续并完成此电路的代码。
单击“模拟”以编译和模拟设计。
编译(逻辑合成)
使用Altera Quartus编译代码以生成电路。Quartus产生大量信息。单击显示Quartus消息以显示/隐藏它们。减少警告的数量是很好的做法,但有时将它们全部删除并不实际。
模拟
对编译后的电路进行模拟,以测试其功能是否正确。HDLBits使用ModelSim并行模拟您的电路和我们的参考解决方案,然后比较模块的输出。模拟报告了两件事:
首先,它报告您的电路是否与参考电路完全匹配(零个“失配”)或发生了多少个“失匹配”。失配是指电路输出与参考输出不匹配的样本数。
第二,当运行测试向量时,它可能会生成显示电路输出的时序图。模拟波形分为三个部分:“输入”、“您的”和“参考”。在正确的电路中,“Your”输出将与“Ref”输出相同。“不匹配”信号告诉哪些样本不匹配。0.2 输出0
⚠:顶级top_module的模块名和端口名不能更改,否则会出现模拟错误。
最终状态
如果电路正确,您将看到状态:成功!。还有一些其他可能性:
编译错误-电路未编译。
模拟错误-电路已成功编译,但模拟未完成。
不正确-电路已编译和模拟,但输出与参考不匹配。
成功!-电路正确
您可以在“我的统计信息”页面上跟踪或分享您的进度。
问题陈述
我们将从一点点HDL开始,以熟悉HDLBits使用的接口。以下是本练习需要构建的回路的描述:
建立一个没有输入和一个输出的电路。该输出应始终驱动1(或逻辑高)。
答案:
module top_module( output one );
// Insert your code here
assign one =1;
endmodule
output Zero(输出0)
0
构建一个无输入、输出为常数0的电路
既然您已经解决了上一个问题,那么让我们看看您是否可以在没有提示的情况下解决一个简单的问题。
⚠:HDLBits使用Verilog-2001 ANSI风格的端口声明语法,因为它更易于阅读并减少了拼写错误。如果愿意,可以使用旧的Verilog-1995语法。例如,以下两个模块声明是可接受的和等效的:
module top_module ( zero );
output zero;
// Verilog-1995
endmodule
module top_module ( output zero );
// Verilog-2001
endmodule
答案:
module top_module(
output zero
);// Module body starts after semicolon
assign zero=0;
endmodule
Verilog Language
Basics
Simple wire
module top_module( input in, output out );
assign out=in;
endmodule
Four wires
module top_module(
input a,b,c,
output w,x,y,z );
assign w=a;
assign x=b;
assign y=b;
assign z=c;
endmodule
Inverter
module top_module( input in, output out );
assign out=!in;
endmodule
AND gate
module top_module(
input a,
input b,
output out );
assign out=a&b;
endmodule
NOR gate
module top_module(
input a,
input b,
output out );
assign out=!(a|b);
endmodule
XNOR gate
module top_module(
input a,
input b,
output out );
assign out = ~a^b;
endmodule
Declaring wires
`default_nettype none
module top_module(
input a,
input b,
input c,
input d,
output out,
output out_n );
wire w1,w2,w3;
assign w1=a&b;
assign w2=c&d;
assign w3=w1|w2;
assign out=w3;
assign out_n=!w3;
endmodule
module top_module (
input p1a, p1b, p1c, p1d, p1e, p1f,
output p1y,
input p2a, p2b, p2c, p2d,
output p2y );
wire w1,w2,w3,w4;
assign w1=p2a&p2b;
assign w2=p2c&p2d;
assign w3=p1a&p1b&p1c;
assign w4=p1e&p1d&p1f;
assign p2y=(w1|w2);
assign p1y=(w3|w4);
endmodule
7458chip
module top_module (
input p1a, p1b, p1c, p1d, p1e, p1f,
output p1y,
input p2a, p2b, p2c, p2d,
output p2y );
assign p1y=(p1a&p1b&p1c)|(p1d&p1e&p1f);
assign p2y=(p1a&p1b)|(p1c&p1c);
endmodule
Vectors
Vectors
Vector3
串联运算符 {a,b,c}
{3’b111, 3’b000} => 6’b111000
{1’b1, 1’b0, 3’b101} => 5’b10101
{4’ha, 4’d10} => 8’b10101010
串联需要知道每个组件的宽度(或者您如何知道结果的长度?因此,{1, 2, 3} 是非法的,并导致错误消息:串联中不允许使用未调整大小的常量。
练习:
给定多个输入向量,将它们连接在一起,然后将它们拆分为多个输出向量。有六个 5 位输入向量:a、b、c、d、e 和 f,总共 30 位输入。有四个 8 位输出向量:w、x、y 和 z,用于 32 位输出。输出应该是输入向量的串联,后跟两个 1 位:
module top_module (
input [4:0] a, b, c, d, e, f,
output [7:0] w, x, y, z );//
assign {w, x, y, z} = {a, b, c, d, e, f,2'b11};
endmodule
Vectorr
给定一个 8 位输入向量 [7:0],反转其位排序。
module top_module(
input [7:0] in,
output [7:0] out
);
assign {out}={in[0],in[1],in[2],in[3],in[4],in[5],in[6],in[7]};
endmodule
Vector4
串联运算符允许将向量连接在一起以形成更大的向量。但有时你想把同一个东西连接在一起很多次,做一些像赋值a = {b,b,b,b,b,b};这样的事情仍然很乏味。复制运算符允许重复向量并将它们连接在一起:
{num{vector}}
这将向量复制数倍。num 必须是常量。两组大括号都是必需的。
例子:
{5{1’b1}} // 5’b11111 (or 5’d31 or 5’h1f)
{2{a,b,c}} // The same as {a,b,c,a,b,c}
{3’d5, {2{3’d6}}} // 9’b101_110_110. It’s a concatenation of 101 with
// the second vector, which is two copies of 3’b110
练习:查看复制运算符的一个常见位置是将较小的数字扩展到较大的数字,同时保留其签名值。这是通过将较小数字的符号位(最高有效位)复制到左侧来完成的。例如,符号扩展 4’b0101 (5) 到 8 位导致 8’b00000101 (5),而符号扩展 4’b1 101 (-3) 到 8 位导致 8’b11111101 (-3)。
构建一个将 8 位数字符号扩展到 32 位的电路。这需要符号位的24个副本(即复制位[7]24次)的串联,后跟8位数字本身。
module top_module (
input [7:0] in,
output [31:0] out );//
assign out = { {24{in[7]}} ,in};
endmodule
Vector5
给定五个 1 位信号(a、b、c、d 和 e),计算 25 位输出向量中的所有 25 个成对一位比较。如果要比较的两位相等,则输出应为 1。
out[24] = ~a ^ a; // a == a, so out[24] is always 1.
out[23] = ~a ^ b;
out[22] = ~a ^ c;
…
out[ 1] = ~e ^ d;
out[ 0] = ~e ^ e;
如图所示,使用复制和串联运算符可以更轻松地完成此操作。
顶部向量是每个输入的 5 个重复的串联
底部向量是 5 个输入串联的 5 个重复
module top_module (
input a, b, c, d, e,
output [24:0] out );//
// The output is XNOR of two vectors created by
// concatenating and replicating the five inputs.
assign out = ~{5{a,b,c,d,e}} ^ {{5{a}},{5{b}},{5{c}},{5{d}},{5{e}},};
endmodule
Module
onnecting Signals to Module Ports
有两种常用的方法将电线连接到端口:按位置或按名称。
By position
按位置将电线连接到端口的语法应该很熟悉,因为它使用类似C的语法。实例化模块时,根据模块的声明从左到右连接端口。例如:
mod_a instance1 ( wa, wb, wc );
这将实例化类型的模块并为其提供实例名称“instance1”,然后将信号(在新模块外部)连接到新模块的第一个端口 (),连接到第二个端口 () 和第三个端口 ()。此语法的一个缺点是,如果模块的端口列表发生更改,则还需要查找并更改模块的所有实例化以匹配新模块。mod_awain1wbin2wcout
By name
按名称将信号连接到模块的端口,即使端口列表更改,电线也能保持正确连接。但是,此语法更详细。
mod_a instance2 ( .out(wc), .in1(wa), .in2(wb) );
上面的行实例化名为“instance2”类型的模块,然后将信号(模块外部)连接到名为 的端口、名为 的端口和名为 的端口。请注意,端口的顺序在这里是无关紧要的,因为无论它在子模块的端口列表中的位置如何,都将连接到正确的名称。另请注意此语法中端口名称前面的句点。mod_awain1wbin2wcout
module top_module ( input a, input b, output out );
mod_a mod_a1 (.out(out),.in1(a),.in2(b));;
endmodule
Module pos
module top_module (
input a,
input b,
input c,
input d,
output out1,
output out2
);
mod_a mod_a1(out1,out2,a,b,c,d);
endmodule
Module name
module top_module (
input a,
input b,
input c,
input d,
output out1,
output out2
);
mod_a mod_a1(.out1(out1),.out2(out2),.in1(a),.in2(b),.in3(c),.in4(d));
endmodule
mulu