数码管
RTL代码
`timescale 1ns / 1ps
module hex8(
clk,
reset_n,
disp_data,
sel,
led
);
input clk;
input reset_n;
input [32-1:0] disp_data;
//选择8位数码管
output reg [8-1:0] sel;
//选择显示数字
output reg [8-1:0] led;
//分频计数1us,对应1kHz
reg [16-1:0] div_cnt;
always@(posedge clk or negedge reset_n)begin
if(reset_n==0)
div_cnt<=0;
else if(div_cnt>=25000-1)
div_cnt<=0;
else
div_cnt<=div_cnt+1;
end
reg clk_1k;
always@(posedge clk or negedge reset_n)begin
if(reset_n==0)
clk_1k<=0;
else if(div_cnt==50000-1)
clk_1k<=1;
else
clk_1k<=0;
end
//用于选择数码管的计数
reg [3-1:0] num_cnt;
always@(posedge clk or negedge reset_n)begin
if(reset_n==0)
num_cnt<=0;
else if(clk_1k)
num_cnt<=num_cnt+1;
end
always@(posedge clk)
case(num_cnt)
0: sel<=8'b0000_0001;
1: sel<=8'b0000_0010;
2: sel<=8'b0000_0100;
3: sel<=8'b0000_1000;
4: sel<=8'b0001_0000;
5: sel<=8'b0010_0000;
6: sel<=8'b0100_0000;
7: sel<=8'b1000_0000;
endcase
//从32位data中提取对应数码管位数上的显示数字
//直接将data和数码管相连接
reg [4-1:0] disp_temp;
always@(posedge clk)begin
case(num_cnt)
7:disp_temp<=disp_data[3:0];
6:disp_temp<=disp_data[7:4];
5:disp_temp<=disp_data[11:8];
4:disp_temp<=disp_data[15:12];
3:disp_temp<=disp_data[19:16];
2:disp_temp<=disp_data[23:20];
1:disp_temp<=disp_data[27:24];
0:disp_temp<=disp_data[31:28];
default:;
endcase
end
//数码管显示数字和对应的编码的映射
always@(posedge clk)
case(disp_temp)
0: led<=8'hc0;
1: led<=8'hf9;
2: led<=8'ha4;
3: led<=8'hb0;
4: led<=8'h99;
5: led<=8'h92;
6: led<=8'h82;
7: led<=8'hf8;
8: led<=8'h80;
9: led<=8'h90;
4'ha: led<=8'h88;
4'hb: led<=8'h83;
4'hc: led<=8'hc6;
4'hd: led<=8'ha1;
4'he: led<=8'h86;
4'hf: led<=8'h8e;
default:led<=8'h00;
endcase
endmodule
代码解析
- 在verilog实现中,这种case语句的使用很常见
- 在从系统时钟进行分频操作中,直接使用always块生成的分频clk_1k基本不可用,时钟质量较差,这一部分要学习时钟相关内容
RTL代码,使用了74HC595芯片
`timescale 1ns / 1ps
//74HC595驱动模块,verilog语法不允许首位为数字
module HC595_driver(
clk,
reset_n,
Data,
S_EN,
SH_CP,
ST_CP,
DS
);
input clk;
input reset_n;
input [16-1:0] Data;
input S_EN;
//74HC595芯片手册定义的三个信号
output reg SH_CP;
output reg ST_CP;
output reg DS;
parameter CNT_MAX = 2;
//数据寄存操作
//防止在数据发送或接收过程中发生变化
reg [16-1:0] r_Data;
always@(posedge clk)
if(S_EN)
r_Data<=Data;
//分频计数
//对系统时钟进行二分频
//具体()分频由CNT_MAX参数确定
reg [8-1:0] divider_cnt;
always@(posedge clk or negedge reset_n)begin
if(reset_n==0)
divider_cnt<=0;
else if(divider_cnt==CNT_MAX-1'b1)
divider_cnt<=0;
else
divider_cnt<=divider_cnt+1;
end
//周期脉冲
//每当分频计满,则脉冲一次
wire sck_plus;
assign sck_plus = (CNT_MAX-1'b1);
//SH_CP的边缘计数,每一次周期计数一次
reg [6-1:0] SHCP_EDGE_CNT;
always@(posedge clk or negedge reset_n)begin
if(reset_n==0)
SHCP_EDGE_CNT<=0;
else if(sck_plus)
if(SHCP_EDGE_CNT==32)
SHCP_EDGE_CNT<=0;
else
SHCP_EDGE_CNT<=SHCP_EDGE_CNT+1'b1;
end
//直接对于32位波形定义
//避免使用div_cnt产生的时钟信号质量过差的问题
always@(posedge clk or negedge reset_n)begin
if(reset_n==0)begin
SH_CP<=0;
ST_CP<=0;
DS<=0;
end
else begin
case(SHCP_EDGE_CNT)
0: begin SH_CP<=0;ST_CP<=1'd0;DS<=r_Data[15]; end
1: SH_CP<=1'd1;
//下降沿的时候进行数据传输(为什么不是常见的上升沿?)
//理解:fpga传输data,74HC595接收data
//这里在写fpga代码,当在下降沿把data传输过去时
//74HC595接收时就在上升沿!
2: begin SH_CP<=0;DS<=r_Data[14]; end
3: SH_CP<=1'd1;
4: begin SH_CP<=0;DS<=r_Data[13]; end
5: SH_CP<=1'd1;
6: begin SH_CP<=0;DS<=r_Data[12]; end
7: SH_CP<=1'd1;
8: begin SH_CP<=0;DS<=r_Data[11]; end
9: SH_CP<=1'd1;
10: begin SH_CP<=0;DS<=r_Data[10]; end
11: SH_CP<=1'd1;
12: begin SH_CP<=0;DS<=r_Data[9]; end
13: SH_CP<=1'd1;
14: begin SH_CP<=0;DS<=r_Data[8]; end
15: SH_CP<=1'd1;
16: begin SH_CP<=0;DS<=r_Data[7]; end
17: SH_CP<=1'd1;
18: begin SH_CP<=0;DS<=r_Data[6]; end
19: SH_CP<=1'd1;
20: begin SH_CP<=0;DS<=r_Data[5]; end
21: SH_CP<=1'd1;
22: begin SH_CP<=0;DS<=r_Data[4]; end
23: SH_CP<=1'd1;
24: begin SH_CP<=0;DS<=r_Data[3]; end
25: SH_CP<=1'd1;
26: begin SH_CP<=0;DS<=r_Data[2]; end
27: SH_CP<=1'd1;
28: begin SH_CP<=0;DS<=r_Data[1]; end
29: SH_CP<=1'd1;
30: begin SH_CP<=0;DS<=r_Data[0]; end
31: SH_CP<=1'd1;
32: ST_CP<=1'd1;
default:
begin
SH_CP<=0;
ST_CP<=0;
DS<=0;
end
endcase
end
end
endmodule
代码解析
- 在使用芯片时,一定要通过芯片手册查询相关时序,从而才能实现相关的RTL代码
- 对于74HC595芯片规定的波形,这里代码没有先分频,而是直接将每一bit都自行拟定赋值,从而规避时钟质量过差的问题
测试平台
`timescale 1ns / 1ps
module hex8_tb();
reg clk;
reg reset_n;
reg [32-1:0] disp_data;
wire [8-1:0] sel;
wire [8-1:0] led;
hex8 hex8(
clk,
reset_n,
disp_data,
sel,
led
);
initial begin
clk = 1;
forever #10 clk = ~clk;
end
initial begin
reset_n=0;
disp_data=32'h0000_0000;
#201;
reset_n=1;
#2000;
disp_data=32'h1234_abcd;
#10_000_000;
disp_data=32'h5678_4321;
#10_000_000;
$stop;
end
endmodule