以下作为个人学习杂记,不具有严格条理层次性。
学习内容:
今天主要进行了 对昨天呼吸灯的实验的仿真书写,波形观察分析,按键的实现,以及对数码管知识的回顾,还有就是仿真器的进一步认识,简化,还有对vscode的代码补全库的扩展,对配置引脚的保存(方便下次复用)。
今天还有个巨大收获,就是基本上理清了reg,wire,paramter的区别,使用场景等。
疑惑产生及解决:
数码管,怎么实现亮灭变化,为什么非要弄那么多标志位,状态位,不能把对应某个状态的值进行赋予,然后省略状态标志位呀。
解决:效果的实现,更重要的是考验功能剥离,分层能力。本次实现的数码管显示,仅仅是从0到f的16进制显示。分析后,其实就三步 数字显示,数字切换,显示定时。 则,自然的就分成了三个模块,计时模块,标志显示模块,综合实现模块。
通过这次实验,可见,对目标任务的分层显得尤为重要,因为分层后,程序书写将更加清晰。后期维护也更方便,而且程序,模块之间的复用性也好。
而对于标志位,其实在书写过程,或读懂后,就发现是必不可少的,因为标致位有个作用是不能被替代,或者说是忽略的。标致位可以有很好的传递性,与复用性!!也正是因为标致位的存在,让模块化成为可能,让一个个阶段性成果,拼成最终任务。 而且有些地方没了标志位是无法进行的。例如,本实验如果没有num标致位进行标志各个状态,则当时钟计满0.2s时,仅可判断需要切换下一个数码管值,却不能很准确或很灵活定位是哪一个数码管值(即应该显示什么内容)。
所以这个问题是解决了的。
以下是详细内容:
仿真软件标志的认识:
仿真器,上下升沿对齐符号,这几个可以增减对齐黄线,更便于分析。
在仿真过程出现个问题(也是老师提出的),就是莫名的出现了一小段高电平,即按键按下后,没有立刻变为我们按下后led应该呈现的电平(低电平)
通过与下面对比发现,是因为,按键按下后,虽然应该状态切换,但是按下时刻,state仍然处于上一个状态还没来得及立刻改变。
即,先理清程序最初的设计逻辑,state是控制哪一个某一个亮灯的状态(静态),不同key按下,控制的是不同led状态配合下的动态过程(动态)。在开始运行后,不断工作的是用于状态切换的时间计数器。
所以,这里,当按键按下的时候,实际上处于的状态并不是处于第一个00的状态,而是处于11状态。下面这代码就是(部分)不同状态对应的led电平情况。
else if (key[0] == 0) begin
case (state)
2'd0: led <= 4'b0001;
2'd1: led <= 4'b0010;
2'd2: led <= 4'b0100;
2'd3: led <= 4'b1000;
default: ;
endcase
end
从仿真图可以看到,当模拟key0按下的时候,state为11,即状态4,也就是led=1000,所以此时会有led3短暂的高电平。
接下来就是数码管的认识:
共阴极:高电平点亮
共阳极(低电平点亮)(本次开发板中的数字)
以下是要用到的对应真值表。
具体的数码管设计过程今天比较简单,并且再文首已经提过了,就不再赘述。
接下来就是,个人感觉收获很大的地方:reg,wire,parameter的应用场景等的不同。
Q:什么时候定义为reg,什么时候wire的最新理解。
A:常常就是在通过一定计算后,或者一定运行后的数据,且后续还会用到,需要传递到其他模块,则会用reg存储,即,一般模块的输出部分
,但最终的输出一般不需要reg类型,因为不需要再存储了,直接输出就可。 。
还有可以一定程度上去区分的方式是,reg类型的变量常常作为被赋值的对象,其值会经常改变。即 看到
、 ,
就知道,这两个变量一定是 reg类型。
Wire 类型的,则有点与reg相反的意思,wire常常给其他变量赋值, (也可以想成两个模块的桥梁,即 变量a是上个模块的output,是reg,在下个模块就常常为wire类型)
很少被赋值,如果非要被赋值,那更多的时候,也是再原本定义的模块中,需要更新为一个新的值,然后继续作为本模块的下一个小部分,继续使用(即:不存在 被赋值后,直接输出的情况) wire 类型,要被赋值更新的话,要用assign关键字进行修饰,然后赋值。
一般被赋值后后续还会使用。
而参数类型 parameter 则是在要使用具体值的时候,即常值,的时候,会用这个。类似C种define一个常值 。
综上:reg常作为模块运行的最终产物,然后进行存储,等待传递;在模块中常作为被赋值的对象。
Wire 常作为 引入的信号,标志,传递结果。用于判断条件,或对reg变量的赋值,而不作为输出。如果中途要更新wire的值,则用assign关键字进行修饰。
Parameter常作为 常量。 作为条件判断,给其他变量赋值等。
下面就是今天涉及到的代码:
呼吸灯仿真程序
module breath_fsm_led_tb();
reg clk;
reg rst_n;
wire [3:0] led;
// wire led;
parameter cnt_us = 3'd4;
parameter cnt_ms = 4'd9;
parameter cnt_s = 4'd9;
parameter CYCLE = 5'd19;
always #(CYCLE/2) clk =~clk ;
initial begin
clk = 1'b0;
rst_n = 1'b0;//复位开始
#(CYCLE/2);
rst_n = 1'b1;//复位结束
#(20000);//5*10*10*20*2
$stop;
end
breath_fsm_led #(
.cnt_us(cnt_us),
.cnt_ms(cnt_ms),
.cnt_s(cnt_s)
) u_breath_fsm_led(
.clk(clk),
.rst_n(rst_n),
.led(led)
);
endmodule
按键控制led灯:
module key_led(
input clk,
input rst_n,
input [3:0]key,
output reg[3:0] led
);
parameter TIME = 24'd9_999_999;
reg [23:0] cnt;
reg [1:0] state;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt <= 24'd0;
end
else if(cnt == TIME)begin
cnt <=24'd0;
end
else begin
cnt <= cnt + 1'd1;
end
end
//状态切换
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <=2'd0;
end
else if (cnt == TIME)begin
state <= state +1'd1;
end
else begin
state <=state;
end
end
//判断按键值
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
led <= 4'b0000;
end
else if (key[0] == 0) begin
case (state)
2'd0: led <= 4'b0001;
2'd1: led <= 4'b0010;
2'd2: led <= 4'b0100;
2'd3: led <= 4'b1000;
default: ;
endcase
end
else if (key[1] == 0) begin
case (state)
2'd0: led <= 4'b1000;
2'd1: led <= 4'b0100;
2'd2: led <= 4'b0010;
2'd3: led <= 4'b0001;
default: ;
endcase
end
else if (key[2] == 0) begin
case (state)
2'd0: led <= 4'b1111;
2'd1: led <= 4'b0000;
2'd2: led <= 4'b1111;
2'd3: led <= 4'b0000;
default: ;
endcase
end
else if (key[3] == 0) begin
case (state)
2'd0: led <= 4'b1111;
2'd1: led <= 4'b1111;
2'd2: led <= 4'b1111;
2'd3: led <= 4'b1111;
default: ;
endcase
end
else begin
led <= 4'b0000;
end
end
endmodule
按键的 仿真程序
`timescale 1ns/1ns
module key_led_tb();
reg clk;
reg rst_n;
reg [3:0] key;//自己产生的信号,用reg
wire [3:0] led; //直接传递过去的信号,用wire
parameter TIME = 4'd9;
parameter CYCLE = 5'd19 ;
always #(CYCLE/2) clk = ~clk;
initial begin
clk = 1'b0;
rst_n = 1'b0;
#(CYCLE/2);
rst_n = 1'b1;
#(4*TIME*CYCLE);
key = 4'b1110;
#(4*TIME*CYCLE);
key = 4'b1101;
#(4*TIME*CYCLE);
key = 4'b1011;
#(4*TIME*CYCLE);
key = 4'b0111;
#(4*TIME*CYCLE);
$stop;
end
key_led #(.TIME(TIME)) u_key_led(
.clk(clk),
.rst_n(rst_n),
.key(key),
.led(led)
);
endmodule
数码管显示十六进制数:
时间计数文件:
module time_count(//到达一定时间后输出一个标识符
input clk,
input rst_n,
output reg flag
);
parameter TIME = 26'd49_999_999;
reg[25:0]cnt;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt <= 26'd0;
flag <= 1'b1;
end
else if (cnt == TIME) begin
cnt <= 26'd0;
flag <= 1'b1;
end
else begin
cnt <= cnt + 1'd1;
flag <= 1'b0;
end
end
endmodule
数码管内容控制及切换
module sel_led_static(//数码显示
input clk,
input rst_n,
input flag, //计时标志位, 为1,则切换为下一个内容
output reg[5:0]sel,//位选信号
output reg[7:0]seg//段选信号
);
reg[3:0]num;//16进制的16种 状态标志位(指出要显示的内容)
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
sel <= 6'b111_111;
end
else begin
sel <= 6'b000_000;//共阳极数码管,低电平有效,打开六个数码管
end
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
num <= 4'h0;
end
else if(flag)begin
num <= num + 1'd1;
end
else begin
num <= num;
end
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
seg <= 8'h0;
end
else begin
case (num)
4'h0: seg<= 8'b1100_0000;//匹配到后参考共阳极真值表
4'h1: seg<= 8'b1111_1001;
4'h2: seg<= 8'b1010_0100;
4'h3: seg<= 8'b1011_0000;
4'h4: seg<= 8'b1001_1001;
4'h5: seg<= 8'b1001_0010;
4'h6: seg<= 8'b1000_0010;
4'h7: seg<= 8'b1111_1000;
4'h8: seg<= 8'b1000_0000;
4'h9: seg<= 8'b1001_0000;
4'ha: seg<= 8'b1000_1000;
4'hb: seg<= 8'b1000_0011;
4'hc: seg<= 8'b1100_0110;
4'hd: seg<= 8'b1010_0001;
4'he: seg<= 8'b1000_0110;
4'hf: seg <= 8'b1000_1110;
default :seg <= 8'b1100_0000;
endcase
end
end
endmodule
数码管显示最终实现 顶层文件
module top_sel_led_static(
input clk,
input rst_n,
output [5:0] sel,
output [7:0] seg
);
parameter TIME = 26'd24_999_999;//0.5s
wire flag_reg;
time_count #(.TIME(TIME))u_time_count(
.clk(clk),
.rst_n(rst_n),
.flag(flag_reg)
);
sel_led_static u_sel_led_static(
.clk(clk),
.rst_n(rst_n),
.flag(flag_reg),
.seg(seg),
.sel(sel)
);
endmodule
以上三个文件都是数码显示的时候必须的,并且注意的是,再编译前,记得将最后哪个文件设置为top文件
vscode扩展代码:
{
// Place your snippets for verilog here. Each snippet is defined under a snippet name and has a prefix, body and
// description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. Placeholders with the
// same ids are connected.
// Example:
// "Print to console": {
// "prefix": "log",
// "body": [
// "console.log('$1');",
// "$2"
// ],
// "description": "Log output to console"
// }
"Jsq": {
"prefix": "Jsq(计数器)",
"body": [
"always @(posedge clk or negedge rst_n)begin "
" if(!rst_n)begin"
" cnt <= 0;"
" end "
" else if(add_cnt)begin "
" if(end_cnt)begin "
" cnt <= 0;"
" end"
" else begin "
" cnt <= cnt + 1;"
" end "
" end"
" else begin"
" cnt <= cnt;"
" end"
"end "
""
"assign add_cnt = ;"
"assign end_cnt = add_cnt && cnt == ;"
],
"description": "counter"
}
"Shixu": {
"prefix": "Shixu(时序逻辑电路)",
"body": [
"always @(posedge clk or negedge rst_n)begin "
" if(!rst_n)begin"
" <= 0;"
" end "
" else if()begin "
" "
" end "
" else begin "
" "
" end "
"end"
],
"description": "shixu"
}
"Zuhe": {
"prefix": "Zuhe(组合逻辑电路)",
"body": [
"always @(*)begin "
" if()begin"
" = 0;"
" end "
" else if()begin "
" "
" end "
" else begin "
" "
" end "
"end"
],
"description": "Zuhe"
}
"Module": {
"prefix": "Module(模块端口定义)",
"body": [
"module module_name( "
" input clk ,"
" input rst_n ,"
" input din ,"
" input [n:0] din ,"
" input [n:0] din ,"
" output reg [n:0] dout ,"
" output reg [n:0] dout ,"
"); "
" //参数定义 "
" "
" //中间信号定义 "
" reg [] signal0;"
" reg [] signal1;"
" wire [] signal2;"
" wire [] signal3;"
" "
"endmodule"
],
"description": "Module"
}
"Tb": {
"prefix": "Tb(testbeenth)",
"body": [
"`timescale 1ns/1ns"
" "
"module module_name_tb();"
"//激励信号定义 "
"reg tb_clk ;"
"reg tb_rst_n ;"
"reg tb_din0 ;"
"reg tb_din1 ;"
"reg tb_din2 ;"
"//输出信号定义 "
"wire [n:0] tb_dout0 ;"
"wire [n:0] tb_dout1 ;"
" "
"//时钟周期参数定义 "
" parameter CLOCK_CYCLE = 20; "
" "
"module_name u_madule_name( "
".clk (tb_clk ), "
".rst_n (tb_rst_n ), "
".din0 (tb_din0 ), "
".din1 (tb_din1 ), "
".dout0 (tb_dout0 ), "
".dout1 (tb_dout1 ) "
");"
"//产生时钟 "
"initial tb_clk = 1'b0; "
"always #(CLOCK_CYCLE/2) tb_clk = ~tb_clk; "
" "
"//产生激励 "
"initial begin "
" tb_rst_n = 1'b1; "
" tb_din0 = 0; "
" #(CLOCK_CYCLE*2); "
" tb_rst_n = 1'b0; "
" #(CLOCK_CYCLE*20); "
" tb_rst_n = 1'b1; "
" "
" "
" "
"end "
"endmodule "
],
"description": "Testbeench"
}
"Do": {
"prefix": "Do",
"body": [
"#tcl脚本 自动化仿真 "
"#创建工作库 名为work "
"vlib work "
" "
"#映射工作库 work 到 work "
"vmap work work "
" "
"#编译testbench文件 "
"vlog key_led_tb.v "
" "
"#若要仿真IP核必须添加该文件 "
"#vlog altera_mf.v "
" "
"#编译被仿真文件 "
"#vlog ../rtl/key_led.v "
"#vlog ../rtl/key_filter.v "
"#vlog ../rtl/led.v "
" "
"#对IP核进行仿真 "
"#vlog ../par/ipcore/pll.v "
" "
"#指定仿真顶层 "
"vsim -novopt work.key_led_tb"
" "
"#添加testbench顶层模块中的信号到波形窗
"add wave -position insertpoint sim:key_led_tb//* "
" "
"#表示让该tb_clk信号显示黄色 "
"#add wave -noupdate -color Yellow /follow_led_tb/tb_clk "
" "
"#表示让div_coef信号用十进制tb_led来显示 "
"#add wave -noupdate -radix decimal /follow_led_tb/tb_led "
" "
"#运行2000个时间单位 "
"run 2000 "
" "
"#一直仿真下去 "
"#run -all "
],
"description": "Do"
}
"Zhushi": {
"prefix": "Zhushi(注释模板)",
"body": [
"/**************************************功能介绍***********************************"
"Copyright: "
"Date : "
"Author : "
"Version : "
"Description: "
"*********************************************************************************/"
],
"description": "Zhushi"
}
}
配置步骤:
粘贴代码(如果原文件中有个大括号,中间是注释,则把那些都删除了,再粘贴代码)
效果展示:
(也可以自己在文件里面进行扩展,将常用的语句写入)