【基本資訊】
需求:verilog程式,顯示任意六位字元或數值,包含點號,且能夠按需點亮位數。(學習篇)
晶片型號:cyclone Ⅳ EP4CE10F17C8
數碼管屬性:六位、八段
【最終成果圖】
經過多輪測試,最後程式碼程式滿足設計要求,但結合模擬發現了一個問題,模擬和上機不匹配,當然還是要以上機為準。
【模組例化圖】
這裡就是簡單地賦個初始值,來測試digital模組,最終是要seg_led和seg_sel的顯示變化。
【verilog程式】
1、digital模組
模組化設計,六個位上的值直接拆開做處理,分析起來清晰。sel_cnt表示目前要顯示的位數(從右往左),即sel_cnt = 6代表全亮。dp_cnt表示點號所在位數,若dp_cnt=0則代表沒有點號。clk_2khz為數碼管的重新整理訊號。
module digital
(
input sys_clk ,
input sys_rst ,
input clk_2khz ,
input [3:0] num6,
input [3:0] num5,
input [3:0] num4,
input [3:0] num3,
input [3:0] num2,
input [3:0] num1,
input [2:0] sel_cnt ,
input [2:0] dp_cnt ,
output reg[5:0] seg_sel ,
output reg[7:0] seg_led
);
reg[2:0] sel_cnt_tran;
reg[3:0] num;
reg[2:0] tran1;
//endmodule
定義了幾個變數,sel_cnt_tran用來根據數碼管重新整理訊號更替數碼管的位選,而num用來接引各位上的值,數碼管根據num編號段選訊號。tran1在過程討論部分做解釋。
always @(posedge sys_clk or negedge sys_rst)
begin
if(!sys_rst)
sel_cnt_tran = 3'd0;
else
if(clk_2khz)begin
if(sel_cnt_tran < sel_cnt)begin
tran1 = sel_cnt_tran;
sel_cnt_tran = sel_cnt_tran + 1'b1;
end
else begin
tran1 = sel_cnt_tran;
sel_cnt_tran = 3'd0;
end
end
end
上述程式碼就是位選更替,可以直觀得到,tran1比sel_cnt_tran要晚上一個數碼管重新整理週期。
always @(posedge sys_clk or negedge sys_rst)
begin
if(!sys_rst)
seg_sel <= 6'b111_111;
else if(clk_2khz)begin
case(sel_cnt_tran)
3'd1:begin
seg_sel <= 6'b111_110;
num <= num1;
end
3'd2:begin
seg_sel <= 6'b111_101;
num <= num2;
end
3'd3:begin
seg_sel <= 6'b111_011;
num <= num3;
end
3'd4:begin
seg_sel <= 6'b110_111;
num <= num4;
end
3'd5:begin
seg_sel <= 6'b101_111;
num <= num5;
end
3'd6:begin
seg_sel <= 6'b011_111;
num <= num6;
end
default:begin
seg_sel <= 6'b111_111;
end
endcase
end
end
根據原理圖,數碼管是共陽極,並且位選拉低有效,這裡測試是沒有問題的。
always @(posedge sys_clk or negedge sys_rst)begin
if (!sys_rst)
seg_led = 8'b0;
else begin
case (num)
4'h0 : seg_led = 8'b1100_0000;
4'h1 : seg_led = 8'b1111_1001;
4'h2 : seg_led = 8'b1010_0100;
4'h3 : seg_led = 8'b1011_0000;
4'h4 : seg_led = 8'b1001_1001;
4'h5 : seg_led = 8'b1001_0010;
4'h6 : seg_led = 8'b1000_0010;
4'h7 : seg_led = 8'b1111_1000;
4'h8 : seg_led = 8'b1000_0000;
4'h9 : seg_led = 8'b1001_0000;
4'ha : seg_led = 8'b1000_1000;
4'hb : seg_led = 8'b1000_0011;
4'hc : seg_led = 8'b1100_0110;
4'hd : seg_led = 8'b1010_0001;
4'he : seg_led = 8'b1000_0110;
4'hf : seg_led = 8'b1000_1110;
default : seg_led = 8'b1100_0000;//多餘
endcase
if(dp_cnt == (tran1))begin //確定點號,這裡不是sel_cnt_tran
seg_led = seg_led & 8'b0111_1111;
end
end
end
2、clk_2khz模組
module clk_2khz
(
input sys_clk ,
input sys_rst ,
output reg clk_out
);
parameter cnt_2khz_max = 25_000;
reg[14:0] cnt;
clk_2khz時鐘產生很簡單,cnt_2khz_max = 1/2_000x50_000_000,在這就放下程式碼。注意兩個問題:一個是不要直接posedge clk_2khz方式來讀上升沿,另外儘量不要採用取反方式產生分頻時鐘訊號,如果是取反,可以採用wire en;assign en = d1 & d0來讀邊沿訊號。
always @(posedge sys_clk or negedge sys_rst)begin
if(!sys_rst)begin
cnt <= 15'd0;
clk_out <= 1'b0;
end
else if(cnt <(cnt_2khz_max-1'b1))begin
cnt <= cnt + 1'b1;
clk_out <= 1'b0;
end
else begin
cnt <= 15'd0;verilog
clk_out <= 1'b1;
end
end
endmodule
3、頂部例化測試
module digital_top (
input sys_clk ,
input sys_rst ,
output [5:0] seg_sel ,
output [7:0] seg_led
);
wire clk_2khz_tran;
reg[3:0] num1 = 4'd2;
reg[3:0] num2 = 4'h1;
reg[3:0] num3 = 4'd5;
reg[2:0] sel_cnt = 3'd6;
reg[2:0] dp_cnt = 3'd6;
digital digital_inst(); //例化省略
clk_2khz clk_generator1();
endmodule
4、Modelsim模擬
`timescale 1ns/1ns
module digital_tb();
parameter T1 =20 ;
parameter T2 =200;
parameter max = 14;
reg sys_clk;
reg clk_2khz;
reg sys_rst;
reg[3:0] num1 = 4'h1;
reg[3:0] num2 = 4'h1;
reg[3:0] num3 = 4'h1;
reg[2:0] sel_cnt = 3'd6;
reg[2:0] dp_cnt = 3'd6;
reg[3:0] cnt;
wire[7:0] seg_led;
wire[5:0] seg_sel;
always # (T1/2) sys_clk <= ~sys_clk;
always @(posedge sys_clk or negedge sys_rst)begin
if(!sys_rst)
cnt <= 4'd0;
else begin
if(cnt < max)begin
cnt <= cnt + 1'b1;
clk_2khz <= 1'b0;
end
else begin
cnt <= 4'd0;
clk_2khz <= 1'b1;
end
end
end
initial begin
sys_clk <=1'b0;
sys_rst <=1'b0;
#50 sys_rst <=1'b1;
end
digital digital_inst();//例化省略
endmodule
【過程討論】
首先由於自己疏忽,數碼管位選重新整理位置採用的是非阻塞賦值的方法,透過用sel_cnt_tran判斷為真的話,透過模擬(下圖)觀察到,位選和段選不匹配,就是說,點號的位置不正確,現象是位選落後了。
然後想著將賦值方法改為阻塞賦值,還是有問題。強制更改,設定判斷條件為if(dp_cnt == (sel_cnt_tran-1’b1))看看,顯然,這樣雖然有效(圖在下面),但只能保證最終的點號位置有限,比如dp_cnt = 6就有問題,因為sel_cnt_tran在6後賦值是0,達不到條件。後面藉助變數tran1在sel_cnt_tran重新整理處賦值使其落後一個週期,模擬顯示不正確,但上機測試有效。