B站大学的课程【Verilog零基础入门】 虽然写的0基础,但是并没有像其他语言那样讲解,建议不管什么先跟写,熬过两集慢慢就会理解,不会的问题也可以去搜
仿真软件为modelsim
百度云:链接: https://pan.baidu.com/s/1iD-Ryn6DVSujovU7_Tb9ew 提取码: 4t91 复制这段内容后打开百度网盘手机App,操作更方便哦)
破解方式
- 运行MentorKG.exe,生成LICENSE.txt,把后缀改成.dat,
- 将MentorKG.exe和patch_dll.bat放在安装目录win64文件夹下
- dos中运行patch_dll.bat
- LICENSE.dat文件放在安装目录win64下面
- 配置下环境变量重启,添加环境变量名LM_LICENSE_FILE,值为LICENSE.dat路径
软件仿真的基础使用上面教程的第一集就有,很简单
基本逻辑门
非门
注意,仿真软件基本都不支持中文,一编译或重新打开很容易中文乱码,注释建议写英文,开始可能会吃力,用翻译软件,用的多了也就会一些了。下面的中文注释方便阅读
`timescale 1ns/10ps // 时间单位/精度
module inv(A,Y);
input A;
output Y;
assign Y=~A;
endmodule
// testbench of inv 测试函数
module inv_tb();
reg aa; //输入定义reg register寄存器
wire yy; //输出定义wire wire导线
inv inv(.A(aa), .Y(yy));
initial begin
aa<=0; //将0赋值给aa
#10 aa<=1; //等10个时间单位后
#10 aa<=0;
#10 aa<=1;
#10 aa<=0;
#10 $stop; //仿真停止命令
end
endmodule
4位与非门
`timescale 1ns/10ps
module nand_gate(a,b,y);
input[3:0] a; //这个和软件编程语言有些不同,[3:0]代表4位数组 例如 1101 ,左边是高位下标3的位
input[3:0] b;
output[3:0] y;
assign y=~(a&b);
endmodule
module nandgate_tb();
reg[3:0] aa,bb;
wire[3:0] yy;
nand_gate nand_gate(.a(aa),.b(bb),.y(yy));
initial begin
aa<=4'b0000;bb<=0; //4'b 代表有4位 b为binary二进制
#10 aa<=4'b0000;bb<=4'b0000;
#10 aa<=4'b0100;bb<=4'b1000;
#10 aa<=4'b0001;bb<=4'b0010;
#10 aa<=4'b0100;bb<=4'b1000;
#10 $stop;
end
endmodule
组合逻辑
二选一选择器
计组中的多路选择器
有两种实现方式
方式一
module fn_swv1(a,b,sel,y);
input a;
input b;
input sel;
output y;
assign y=sel?(a^b):(a&b);
endmodule
module fn_sw_tb();
reg a,b,sel;
wire y;
fn_swv1 fn_swv1(.a(a),.b(b),.sel(sel),.y(y));
initial begin
a<=0;b<=0;sel<=0;
#10 a<=1;b<=0;sel<=0;
#10 a<=1;b<=1;sel<=0;
#10 a<=1;b<=1;sel<=1;
#10 a<=0;b<=1;sel<=0;
#10 a<=0;b<=1;sel<=1;
#10 a<=0;b<=0;sel<=1;
方法二
module fn_swv2(a,b,sel,y);
input a;
input b;
input sel;
output y;
reg y; //输出需要定义为reg
always@(a or b or sel) begin //输入变量不能少
if (sel==1) begin
y<=a^b;
end
else begin
y<=a&b;
end
end
endmodule
module fn_sw_tb2();
reg a,b,sel;
wire y;
fn_swv2 fn_swv2(.a(a),.b(b),.sel(sel),.y(y));
initial begin
a<=0;b<=0;sel<=0;
#10 a<=1;b<=0;sel<=0;
#10 a<=1;b<=1;sel<=0;
#10 a<=1;b<=1;sel<=1;
#10 a<=0;b<=1;sel<=0;
#10 a<=0;b<=1;sel<=1;
#10 a<=0;b<=0;sel<=1;
#10 a<=1;b<=0;sel<=1;
#10 $stop;
end
endmodule
sel是多为的情况,当sel2位时就有4种情况
`timescale 1ns/10ps
module fn_swv3(a,b,sel,y);
input a;
input b;
input[1:0] sel; //一定注意这里,容易忘
output y;
reg y;
always@(a or b or sel) begin //看的出来和C非常的相似
case(sel)
2'b00:begin y<=a&b;end
2'b01:begin y<=a|b;end
2'b10:begin y<=a^b;end
2'b11:begin y<=~(a^b);end
endcase
end
endmodule
module fn_sw_tb3();
reg[3:0] param; //四位的para参数 xxxx
wire y;
fn_swv3 fn_swv3(.a(param[0]),.b(param[1]),.sel(param[3:2]),.y(y)); //有点类似数组,不过这里是取第几位上的二进制数
initial begin
param<=0;
#200 $stop; //200个单位后停止运行
end
always #10 param<=param+1; //每过10个单位param+1
endmodule
源码转补码
`timescale 1ns/10ps
module comp_conv(a,c);
input[7:0] a;
output[7:0] c;
wire[6:0] b; //value bit after inversion
wire[7:0] y; //complement of negative number
assign b=~a[6:0];
assign y[6:0]=b+1;
assign y[7]=a[7]; //sign bit hold
assign c=a[7]?y:a;
endmodule
module comp_conv_tb();
reg[7:0] aa;
wire[7:0] cc;
comp_conv comp_conv(.a(aa),.c(cc));
initial begin //正常我们为了验证要把所有的情况都列出,这里图个省事
aa<=8'b00000000;
#50 aa<=8'b00000001;
#50 aa<=8'b00000001;
#50 aa<=8'b10000111;
#50 aa<=8'b10110000;
#50 $stop;
end
endmodule
还可以改写
module comp_conv(a,c);
input[7:0] a;
output[7:0] c;
wire[7:0] y; //complement of negative number
assign y={a[7],~a[6:0]+1};
assign c=a[7]?y:a; //其实还可以进一步改写
endmodule
7段数码管译码器
// -------
// | a |
// f| |b
// |-------|
// | g |
// e| |c
// -------
// d
//
`timescale 1ns/10ps
module seg_dec(num,ag);
input[3:0] num;
output[6:0] ag;
reg[6:0] ag; // a,b,c,d,e,f,g
always@(num)begin
case(num)
4'd0: begin ag<=7'b111_1110;end
4'd1: begin ag<=7'b011_0000;end
4'd2: begin ag<=7'b110_1101;end
4'd3: begin ag<=7'b111_1100;end
4'd4: begin ag<=7'b011_0011;end
4'd5: begin ag<=7'b101_1011;end
4'd6: begin ag<=7'b101_1111;end
4'd7: begin ag<=7'b111_0000;end
4'd8: begin ag<=7'b111_1111;end
4'd9: begin ag<=7'b111_1010;end
default: begin ag<=7'b000_0001;end
endcase
end
endmodule
module seg_dec_tb();
reg[3:0] n;
wire[6:0] y;
seg_dec seg_dec(.num(n),.ag(y));
initial begin
n<=0;
#100 $stop;
end
always #10 n<=n+1;
endmodule
时序逻辑
`timescale 1ns/10ps
module counter(clk,res,y); //时许逻辑第一个clk第二个res,固定
input clk;
input res;
output[7:0] y;
reg[7:0] y;
wire[7:0] sum; //
assign sum=y+1;
always@(posedge clk or negedge res) begin //clk的上升沿 res的下降沿 触发
if(~res)begin
y<=0;
end
else begin
y<=sum;
end
end
endmodule
module counter_tb();
reg clk,res;
wire[7:0] y;
counter counter(.clk(clk),.res(res),.y(y));
initial begin //初始值一定要设定
clk<=0;res<=0;
#17 res<=1; //17这个值是不固定的,只是用于延迟一点开始工作
#600 $stop;
end
always #5 clk<=~clk; //时钟的实现
endmodule
其效果是在时钟的上升沿将sum传给y
res的是重置,如果res=0了,他会将y重置为0,res=1时,则一直进行+1操作
四级伪随机码发生器
`timescale 1ns/10ps
module m_gen(clk,res,y);
input clk;
input res;
output y;
reg[3:0] d;
assign y=d[0];
always@(posedge clk or negedge res) begin
if(~res)begin
d<=4'b1111; //不允许全0
end
else begin
d[2:0]<=d[3:1]; //右移一位,有>>运算符,不建议用,老师为解释
d[3]<=d[3]+d[0]; //模二加,效果类似异或,暂留个问题?
end
end
endmodule
module m_gen_tb();
reg clk,res;
wire y;
m_gen m_gen(.clk(clk),.res(res),.y(y));
initial begin
clk<=0;res<=0;
#17 res<=1; //
#400 $stop;
end
always #5 clk<=~clk;
endmodule
秒计数器
这东西挺好玩的
我们有一个23M的时钟,我们在每一个时钟上升将con_t+1,con_t的范围时0-23999999
每次从0到23999999说明走了24000000个时钟周期,也就是1秒,那么我们标记con_t=1,每次到1说明过了1秒。此时已经实现了秒单位。计数器的范围0-9,就像红路灯倒计时一样循环,当到9的时候重置为0
`timescale 1ns/10ps
module s_counter(clk,res,s_num);
input clk;
input res;
output[3:0] s_num;
parameter freq_clk=24; //24M
reg[24:0] con_t;
reg s_pulse;
reg[3:0] s_num;
always@(posedge clk or negedge res) begin
if(~res)begin
con_t<=0;
s_pulse<=0;
s_num<=0;
end
else begin
//if(con_t==freq_clk*1000000-1)begin
if(con_t==freq_clk*1000-1)begin //方便我们仿真时查看结果
con_t<=0;
end
else begin
con_t<=con_t+1;
end
end
if(con_t==0)begin
s_pulse<=1;
end
else begin
s_pulse<=0;
end
if(s_pulse==1)begin
if(s_num==9)begin
s_num<=0;
end
else begin
s_num<=s_num+1;
end
end
else begin
end
end
endmodule
module s_counter_tb();
reg clk,res;
wire[3:0] s_num;
s_counter s_counter(.clk(clk),.res(res),.s_num(s_num));
initial begin
clk<=0; res<=0;
#18 res<=1;
#1000 $stop;
end
always #5 clk<=~clk;
endmodule
综合
将上面的秒计数器和7段数码管组合,我们就是实现了一个0-9反复显示的数码管
module top(clk,res,ag);
input clk j
input res;
output[6:0] ag;
wire[3:0] s_num; //只是内部链接,用wire
s_counter U1(.clk(clk),.reg(reg),.s_num(s_num)); //U1是实例化名 module在同一个目录下就能扫描到
seg_dec U2(.num(s_num),.ag(ag));
endmodule
秒分频
课上留下的问题
相邻16点相加输出
没学数电有点难搞,看不懂 2022年12月19日