EDA設計(verilog)—— 七段管+字串位移

2020-10-25 07:00:59

問題描述:在8 個7 段管上顯示HELLO_ _ _(可以顯示下劃線或不亮也可),每隔1 秒鐘,字元序列左移或右移一個七段管的位置。系統外部時鐘50 MHz。左/右移位可以通過一個波動開關sw0 來控制。

1、題目解析:

分析1:如何實現這個功能?
     我們需要8個7為的暫存器(或者一個7*8 = 56 位的暫存器)來保留"hello_ _ _"這個字串,每秒對這8個暫存器裡的值做出改變,讓這八個七段管的裡的值發生阻塞賦值,達到移動目的(如果是7*8的暫存器做一個迴圈左移/右移)。

分析2:如果實現每一秒進行一次變化?
     根據系統外部時鐘為50 MHz,我們可以寫一個計數器,時鐘每變化一個週期就記錄一次,一直數到50 M時輸出一個電平。或者每數到25M改變一個讓一個輸出暫存器裡的值發生反轉,那麼對於這個暫存器,他的週期就是1Hz了。

分析3"hello_ _ _"字串怎麼輸入?
     最簡單的我們可以定義一個56位的輸入,把值一股腦的全部塞進去。當然現實生活中我們往往不會這麼高 ,而使用一個7位的暫存器存放要輸入的變數,加上一個3(8個7段管 2*3 =8)位的暫存器來指定輸入要放入的具體位置,有點類似計算機中的資料匯流排和地址匯流排。這樣我們要賦值8次達到將字串寫入暫存器。

2、綜合程式碼:

module show_hello(
	// 基本控制
	input clk50, // clk50 為輸入50Mhz系統外部時鐘
	input reset, // reset 為復位訊號
	input sw0,   // 控制方向變數, 為正值的時候向右變化,為負值的時候向左變化
	
	// 輸入控制
	input ifinput,  // 是否正在輸入, 定義為低位時表示正在輸入
	input[2:0] inp, // input_position 輸入的管子位置 2^3 = 8 從0-7 一共表示8個管子 
	input[6:0] inv, // input_value 輸入的字元型
	
	// 輸出控制
	output reg[55:0] out, // 1-8 一共8個七段管的輸出位置(56位)
	output reg err      // 錯誤標誌位
);

wire clk1;      // 接收內部時鐘產生的1Hz訊號

divclk1hz clock1(
	.reset(ifinput),  // 如果使用者進行了輸入,那麼我們就重新開始計數
	.clk50(clk50),    // 傳入的基本訊號,用於做分頻
	.clk1(clk1)       // 產生的1Hz訊號
);



always @(posedge clk1 or negedge ifinput) begin
	if (!ifinput) begin  // 如果是在輸入狀態下就進行寄存的複製
		case(inp)    // 根據使用者要求改變的位置進行賦值。
			 0: out[6:0]   <= inv;
			 1: out[13:7]  <= inv;
			 2: out[20:14] <= inv;
			 3: out[27:21] <= inv;
			 4: out[34:28] <= inv;
			 5: out[41:35] <= inv;
			 6: out[48:42] <= inv;
			 7: out[55:49] <= inv;
		 endcase
	end else begin
		err=0;
		if(sw0) begin  // 如果沒有輸入,並且sw0 是高電平
			out <= {out[6:0], out[55:7]};// 就進行向右移動
		end else if(!sw0) begin     // 如果沒有輸入,並且sw0 是低電平
			out <= {out[48:0],out[55:49]};// 就進行向左移動
		end else begin
			err=1;
		end 
	end
end

endmodule



/** 分頻模組,每數到25M改變一個讓一個輸出暫存器clk1裡的值發生反轉 */
module divclk1hz(reset,clk50,clk1);
input clk50,reset;   //clk50 為輸入50Mhz 訊號,reset 為復位訊號
output reg clk1;     // 新產生的1hz 訊號
integer i=0;         //50Mhz 頻率下,週期計數器
always@(posedge clk50) begin
	if(!reset) begin 
		i=0;
		clk1 = 0;
	end else begin
		if(i==30) begin i=0; clk1=~clk1;  // 25000000 這裡的i應該=25M,但是為了更方便的展示效果,我將i的值改為了30,這樣七段管裡的數值會改變的更快。
	end
	else i=i+1;
	end
end
endmodule

3、綜合程式碼:

`timescale 10 ns/ 1 ns
// 注意上面的時間,要 設定最小的時間單位應該為 1/f * 0.5 = 1 / 50M * 0.5 = 10^-8s = 10ns
module show_hello_vlg_tst();

reg clk50;
reg ifinput;
reg [2:0] inp;
reg [6:0] inv;
reg reset;
reg sw0;                                              
wire [55:0]  out;
wire err;

show_hello i1 (  
	.clk50(clk50),
	.ifinput(ifinput),
	.inp(inp),
	.inv(inv),
	.out(out),
	.reset(reset),
	.sw0(sw0),
	.err(err)
);


initial begin
	// 初始化方向 和 時鐘
	sw0 = 1; clk50 = 0;
	// 給每一個七段管子賦值,這裡為了讓大家看的清楚,將8個管子的值賦值為了"88 88888","hello__"需要跟改為
	/**
		1001000
		0110000
		1110001
		1110001
		1100010
		1110111
		1110111
		1110111
	*/
	#00; ifinput = 1; inp = 0; inv=7'b0000000;
	#10 ifinput = 0;
	#10; ifinput = 1; inp = 1; inv=7'b0000000;
	#10 ifinput = 0;
	#10; ifinput = 1; inp = 2; inv=7'b1111111;
	#10 ifinput = 0;
	#10; ifinput = 1; inp = 3; inv=7'b0000000;
	#10 ifinput = 0;
	#10; ifinput = 1; inp = 4; inv=7'b0000000;
	#10 ifinput = 0;
	#10; ifinput = 1; inp = 5; inv=7'b0000000;
	#10 ifinput = 0;
	#10; ifinput = 1; inp = 6; inv=7'b0000000;
	#10 ifinput = 0;
	#10; ifinput = 1; inp = 7; inv=7'b0000000;
	#10 ifinput = 0; #1 ifinput = 1;  // 賦值過程結束
	
	// 經過900 個單位時間的等待,觀察輸出的右移過程
	#899 sw0 = 0;
	
	// 再經過600 個單位時間的等待,暫停執行
	#600 $stop;
	
end

always begin
#1 clk50 =~ clk50;
end
endmodule

4、效果展示

在這裡插入圖片描述

我們可以清楚的看見,開始時sw0處於高電位, 輸出的高電平從高位移動向低位(七段管上文字向右移動),當 sw0 的值從高電平轉換為低電平時,輸出的高電平從低位移動向高位(七段管上的文字向左移動)。

5、還可以改進的地方

現在對於我們來說,要向暫存器中寫入「hello_ _ _" 需要使用者知道七段管的表式方法才行,但我們常用的是 ASCLL 碼或者是數值,所以我們可以在寫一個轉化的模組,將使用者傳來的數值或者ascll碼轉化為7短管的顯示陣列,來方便我們的賦值工作。
參考如下:

在這裡插入圖片描述