今天是实训第九天,今天主要是完成了电子表的更加完善的设计。上午设计了闹钟,即到达一定时间后就进行鸣叫,下午进行了自主设计,将电子表设计成可以显示时间,设置时间,设置闹钟时间,并用数码管显示。
收获:
今天主要的收获,就是对 一些模块更加熟悉,慢慢自己学会了书写与应用,如计时模块等,有了更深的理解,基本上能灵活应用。同时还有一些语法问题,latch的产生。还有就是找到了一些可以自学的资源,即:野火fpga官网的一些文档资源,(相关链接)。这对于后续长期学习,以及查错,纠错提供了新渠道思路。以及对后续学其他东西找资源,有了新启发。
同时通过这么些天对fpga的学习,终于对其能够有所辨识,有所与之前接触的stm32等逐渐区别开。
产生疑惑及解决:
Q: 我们这个按键,如key[0]按下后,key[0]变成1,后续呢?后续恢复成0,还是仍然为1?
A:该问题产生,主要是因为其应用于if判断语句。针对我们这个程序,因为是松开按键的时候会产生一个flag信号,也即key[0]变成1,所以这个是一个瞬间的事,也即 再下一次再面对该if语句的时候,就已经变成0了,也即,其没有存储或持续功能。所以也不能再其内部再嵌套一个用key等相关的判断语句。
Q:顶层文件中可以写always吗?
A: 目前理解,应该是不能的,顶层文件只做实例化,所以最好不要出现always用于组合逻辑电路的 写法。
学习杂记,及相关文件设计思路:
在书写电子时钟过程,个人逻辑还是感觉比较清晰。但语法等,还有一些注意点,没能注意到,导致最终没能很好完成任务,其中典型的就是latch的产生。
latch 的产生典型的三种情况:1. always中if没有else 2. always中case没列举完,且没有default、 2. 输出值给自己赋值
If else基本结构
、
在 if-else 条件语句的这种使用形式中没有出现 else 项,这种情况下条件分支语句的执行过程是:如果指定的<条件表达式>成立(也就是这个条件表达式的逻辑值为“ 1”),则执行条件分支语句内给出的“语句或语句块”,然后退出条件分支语句的执行; 如果<条件表达式>不成立(也就是条件的表达式的逻辑值为“0”、“x”、“z”),则不执行条件分支语句内给出的“语句或语句块”,而是直接退出条件语句的执行。这种写法如果在always块中表达组合逻辑时会产生latch,所以不推荐这种写法。
有if 最终一定要有单独的else
If 嵌套写法
Case语句注意的是,不要出现相同的值
学会层次化设计
今天先简单写在这里。
闹钟的代码实现:
顶层文件:top_sel_led_dynamic
module top_sel_led_dynamic(
input clk,
input rst_n,
output beep,
output [5:0] sel,
output [7:0] seg
);
parameter TIME = 26'd19_999;
parameter CNT_SEC = 26'd49_999_999;
parameter CNT_MAX = 24'd14_999_999;//300ms
parameter DO = 16'd47750 ;//1
parameter RE = 16'd42250 ;//2
parameter MI = 16'd37900 ;//3
parameter FA = 16'd37550 ;//4
parameter SO = 16'd31850 ;//5
parameter LA = 16'd28400 ;//6
parameter XI = 16'd25400 ;//7
wire flag;
wire flag_reg;
wire sec;
wire temp;
time_count #(.TIME (TIME))u_time_count(//计数数码切换的时间
.clk (clk),
.rst_n (rst_n),
.flag (flag_reg)
);
time_count #(.TIME (CNT_SEC)) u1_time_count(
.clk (clk),
.rst_n (rst_n),
.flag (sec)
);
sel_led_dynamic u_sel_led_dynamic(//数码管控制模块
.clk (clk),
.rst_n (rst_n),
.flag (flag_reg),
.sec (sec),
.temp (temp),
.sel (sel),
.seg (seg)
);
//实例化音频选择模块
freq_select#(
.CNT_MAX (CNT_MAX),
.DO (DO) ,
.RE (RE) ,
.MI (MI) ,
.FA (FA) ,
.SO (SO) ,
.LA (LA) ,
.XI (XI)
) u_freq_select(
.clk (clk) ,
.rst_n (rst_n),
.flag (flag)
);
//实例化pwm产生模块
gen_pwm u_gen_pwm
(
.clk (clk) ,
.rst_n (rst_n),
.flag (flag) ,
.temp (temp),
.beep (beep)
);
endmodule
计时模块 time_out
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'b0;
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
数码管显示及时间设置:sel_led_dynamic
module sel_led_dynamic(
input clk,
input rst_n,
input flag,
input sec,
output reg temp,
output reg [5:0] sel,
output reg [7:0] seg
);
reg [3:0] cstate;//当前状态
reg [3:0] nstate;//下一状态
reg [3:0] value;//保存数码要显示的数字
reg [16:0] sec_sum;///记录过去的总秒数
wire [1:0] hour_h;
wire [3:0] hour_l;
wire [2:0] min_h;
wire [3:0] min_l;
wire [2:0] sec_h;
wire [3:0] sec_l;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
sec_sum <= 17'd30000; //30000s 08:20:00
temp <= 1'b0;
end
else if(sec_sum == 17'd86399)begin
sec_sum <= 17'd0;
temp <= 1'b0;
end
else if(sec)begin
sec_sum <= sec_sum + 1'd1;
temp <= 1'b0;
end
else if(sec_sum == 17'd30005)begin//闹钟计时达到
temp <= 1'b1;
end
else begin
sec_sum <= sec_sum;
temp <= 1'b0;
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cstate <= 3'd0;
end
else begin
cstate <= nstate;
end
end
always @(*)begin
if(!rst_n)begin
nstate = 3'd0;
end
else begin
case (cstate)
3'd0: if(flag)begin
nstate = 3'd1;
end
else begin
nstate = 3'd0;
end
3'd1: if(flag)begin
nstate = 3'd2;
end
else begin
nstate = 3'd1;
end
3'd2: if(flag)begin
nstate = 3'd3;
end
else begin
nstate = 3'd2;
end
3'd3: if(flag)begin
nstate = 3'd4;
end
else begin
nstate = 3'd3;
end
3'd4: if(flag)begin
nstate = 3'd5;
end
else begin
nstate = 3'd4;
end
3'd5: if(flag)begin
nstate = 3'd0;
end
else begin
nstate = 3'd5;
end
default: nstate = 3'd0;
endcase
end
end
assign hour_h = sec_sum / 3600 / 10;
assign hour_l = sec_sum / 3600 % 10;
assign min_h = sec_sum % 3600 / 60 / 10;
assign min_l = sec_sum % 3600 / 60 % 10;
assign sec_h = sec_sum % 60 / 10;
assign sec_l = sec_sum % 60 % 10;
always @(*)begin
if(!rst_n)begin
sel = 6'b0;
end
else begin
case (cstate)
3'd0: begin
sel = 6'b111_110;
value = hour_h;
end
3'd1: begin
sel = 6'b111_101;
value = hour_l;
end
3'd2: begin
sel = 6'b111_011;
value = min_h;
end
3'd3: begin
sel = 6'b110_111;
value = min_l;
end
3'd4: begin
sel = 6'b101_111;
value = sec_h;
end
3'd5: begin
sel = 6'b011_111;
value = sec_l;
end
default: begin
sel = 6'b000_000;
value = 3'd6;
end
endcase
end
end
always @(*)begin
if(!rst_n)begin
seg = 8'b0;
end
else begin
case (value)
4'd0: seg <= 8'b1100_0000;//匹配到后参考共阳极真值表
4'd1: seg <= 8'b1111_1001;
4'd2: seg <= 8'b1010_0100;
4'd3: seg <= 8'b1011_0000;
4'd4: seg <= 8'b1001_1001;
4'd5: seg <= 8'b1001_0010;
4'd6: seg <= 8'b1000_0010;
4'd7: seg <= 8'b1111_1000;
4'd8: seg <= 8'b1000_0000;
4'd9: seg <= 8'b1001_0000;
default : seg = 8'b00000000;
endcase
end
end
endmodule
pwm波的产生:gen_pwm
module gen_pwm(
input clk,
input rst_n,
input flag,
input temp,
output reg beep
);
parameter TIME = 26'd49_999_999;
reg add_cnt;
reg [25:0] cnt;
reg [5:0] cnt_1m;
wire add_cnt_1s;
wire end_cnt_1s;
wire add_cnt_1m;
wire end_cnt_1m;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
add_cnt <= 1'b0;
end
else if(temp)begin
add_cnt <= 1'b1;
end
else if(end_cnt_1m)begin
add_cnt <= 1'b0;
end
else begin
add_cnt <= add_cnt;
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 0;
end
else if(add_cnt_1s)begin
if(end_cnt_1s)begin
cnt <= 0;
end
else begin
cnt <= cnt + 1;
end
end
else begin
cnt <= cnt;
end
end
assign add_cnt_1s = add_cnt;
assign end_cnt_1s = add_cnt_1s && (cnt == TIME);
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_1m <= 6'd0;
end
else if(add_cnt_1m)begin
if(end_cnt_1m)begin
cnt_1m <= 6'd0;
end
else begin
cnt_1m <= cnt_1m + 1'd1;
end
end
else begin
cnt <= cnt;
end
end
assign add_cnt_1m = end_cnt_1s;
assign end_cnt_1m = add_cnt_1m && cnt_1m == 59;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
beep <= 1'b1;
end
else if(add_cnt)begin
if(flag)begin
beep <= 1'b0;
end
else begin
beep <= 1'b1;
end
end
else begin
beep <= 1'b1;
end
end
endmodule
蜂鸣器音乐鸣叫 设置:freq_select
module freq_select
(
input wire clk ,//时钟信号
input wire rst_n,//复位信号
output reg flag//pwm标志
);
parameter CNT_MAX = 24'd14_999_999;//300ms
parameter NUM_FRE = 6'd33 ;//34个音符
parameter DO = 16'd47750 ;//1
parameter RE = 16'd42250 ;//2
parameter MI = 16'd37900 ;//3
parameter FA = 16'd37550 ;//4
parameter SO = 16'd31850 ;//5
parameter LA = 16'd28400 ;//6
parameter XI = 16'd25400 ;//7
reg [23:0] cnt_delay ;//300ms计数器
reg [5:0] lut_data ;//乐谱数据寄存器
reg [15:0] cnt_freq ;//音符音频计数器
reg [15:0] freq_data ;//音符数据寄存器
wire [14:0] duty_data ;//占空比数据
wire end_note ;//音符结束标志
wire end_spectrum;//音谱结束标志
//单个音符持续时间计时模块
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_delay <= 24'd0;
end
else if(cnt_delay == CNT_MAX)begin
cnt_delay <= 24'd0;
end
else begin
cnt_delay <= cnt_delay + 1'd1;
end
end
//音符计时模块
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_freq <= 16'd0;
end
else if(end_note)begin
cnt_freq <= 16'd0;
end
else begin
cnt_freq <= cnt_freq + 1'd1;
end
end
//音谱计时模块
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
lut_data <= 6'd0;
end
else if(end_spectrum)begin
lut_data <= 6'd0;
end
else if(cnt_delay == CNT_MAX)begin
lut_data <= lut_data + 1'd1;
end
else begin
lut_data <= lut_data;
end
end
//音符查找表模块
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
freq_data <= DO;
end
else begin
case(lut_data)
6'd0: freq_data <= DO;
6'd1: freq_data <= RE;
6'd2: freq_data <= MI;
6'd3: freq_data <= DO;
6'd4: freq_data <= DO;
6'd5: freq_data <= RE;
6'd6: freq_data <= MI;
6'd7: freq_data <= DO;
6'd8: freq_data <= MI;
6'd9: freq_data <= FA;
6'd10: freq_data <= SO;
6'd11: freq_data <= MI;
6'd12: freq_data <= FA;
6'd13: freq_data <= SO;
6'd14: freq_data <= SO;
6'd15: freq_data <= LA;
6'd16: freq_data <= SO;
6'd17: freq_data <= FA;
6'd18: freq_data <= MI;
6'd19: freq_data <= DO;
6'd20: freq_data <= SO;
6'd21: freq_data <= LA;
6'd22: freq_data <= SO;
6'd23: freq_data <= FA;
6'd24: freq_data <= MI;
6'd25: freq_data <= DO;
6'd26: freq_data <= RE;
6'd27: freq_data <= SO;
6'd28: freq_data <= DO;
6'd29: freq_data <= DO;
6'd30: freq_data <= RE;
6'd31: freq_data <= SO;
6'd32: freq_data <= DO;
6'd33: freq_data <= DO;
default:freq_data <= DO;
endcase
end
end
assign duty_data = freq_data >> 1;//占空比50%
assign end_note = cnt_freq == freq_data;
assign end_spectrum = lut_data == NUM_FRE && cnt_delay == CNT_MAX;
//pwm信号产生模块
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
flag <= 1'b0;
end
else begin
flag <= (cnt_freq >= duty_data) ? 1'b1 : 1'b0;
end
end
endmodule