AlteraFPGA學習筆記

Verilog語法

對於數字邏輯電路,主要有組合邏輯和時序邏輯
組合邏輯:多路選擇器、譯碼器、加法器、乘法器等等
時序邏輯:計數器

module模塊

module mux2(a,b,sel,out);

endmodule 

module: 模塊聲明(表示module到endmodule之間的內容是描述模塊的代碼)
mux2: 模塊名字(自定義,相當於此模塊的名稱)
(a,b,sel,out): 括號內是端口列表(類似芯片的引腳定義)

端口類型與位寬

module mux2(a,b,sel,out,io);
	input a;
	input b;
	input sel;
	output out;
	inout io;
endmodule

input a: 表示端口a是輸入端口(類似芯片的輸入引腳)
output b: 表示端口b是輸出端口(類似芯片的輸出引腳)
inout io: 表示端口io是雙向端口(類似芯片的雙向引腳)

module mux2(
	input [7:0] a,
	input [7:0] b,
	input sel,
	output [7:0] out,
	inout io
);

endmodule

與前兩個例子功能相同,只是另一種寫法,將端口的輸入輸出描述放入括號內完成
注:input [7:0] a 表示輸入端口是一個8bit的總線,相當於8根導線,並且編號爲:7 6 5 4 3 2 1 0

內部信號

module mux2(
	input [7:0] a,
	input [7:0] b,
	input sel,
	output [7:0] out,
	inout io
);
	wire oe;
	reg ow;
endmodule

wire oe; 表示名稱爲oe內部信號(相當於芯片內部的一根導線)
reg ow; 表示名稱爲ow內部信號(相當於芯片內部的一位寄存器)
注:導線不存在存儲功能,只有連接功能,所以對於時序邏輯電路無法使用wire型的信號,而組合邏輯電路無法使用reg型變量,但在Verilog語法中任何處於always中的信號均應爲reg型

連續賦值語句

module mux2(
	input [7:0] a,
	input [7:0] b,
	input sel,
	output [7:0] out,
	inout io
);
	wire oe;
	assign out = sel ? a : b;
	
	assign oe = sel;
	assign io = oe ? out[0] : 1'bz;
endmodule

assign out = xxx: 將等號右邊組合邏輯(xxx)的輸出與導線out相連接(對於連續賦值語句assign,只能是wire型的信號)
sel ? a : b: 組合邏輯,sel 爲1 輸出a 反之輸出b
1’bz: 一個高阻態(z)信號
4’b1001; 表示4位二進制1001
4’d9; 表示4位十進制9
4’hc; 表示4位十六進制c
32’hff_00_ff_00 表示32位十六進制ff00ff00

位操作

module mux2(
	input [7:0] a,
	input [7:0] b,
	input sel,
	output [7:0] out,
	inout io
);
	wire oe;
	assign out = sel ? a : b;
	
	assign oe = sel;
	assign io = oe ? out[0] : 1'bz;

	wire [2:0]m;
	assign m = out[5:3];
	//reg [7:0]shift_a;
	//shift_a <= {shift_a[0],shift_a[7:1]};

	wire [3:0] x;
	wire [3:0] y;
	wire [7:0] z;
	z = {x,y};
	z = {2{x}};

	
endmodule 

截取

assign m = out[5:3]; 將out總線編號爲:5 4 3的導線連接到總線m上(默認連接到m的 2 1 0號導線上)

移位

shift_a <= {shift_a[0],shift_a[7:1]}; 時序邏輯內的操作,每次右移一位(循環右移)。
將shift_a總線重新拼接
右移0次shift_a的導線編號:7 6 5 4 3 2 1 0
右移1次shift_a的導線編號:0 7 6 5 4 3 2 1
右移2次shift_a的導線編號:1 0 7 6 5 4 3 2
.
.
.

拼接

z = {x,y}; 將x與y總線進行拼接
拼接前: x( 3 2 1 0 ) y( 3 2 1 0 )
拼接後: z( 7 6 5 4 3 2 1 0 )
其中z[7]與x[3]相接
z[3]與y[3]相接,以此類推

z = {2{x}}; 等效於z = {x,x};
同理z = {3{x}};等效於z = {x,x,x};

運算

對於語法來說+ - * / 四則運算是可以通過的
但是在實際應用中由於資源有限,對於乘法和除法儘量使用加減法和移位代替,尤其是除法操作,可以使用IP核

邏輯運算

&& 邏輯與
|| 邏輯或
! 邏輯非
& 按位與
| 按位或
~ 按位非

時序邏輯

module counter(
	clk,
	en,
	clr,
	cnt_value
);
	input clk;		//時鐘信號
	input en;	//使能信號
	input clr;		//清零信號
	
	output [3:0] cnt_value;
	
	reg [3:0] cnt;	//4位計數用寄存器

	always@(posedge clk or posedge clr)	//clk上升沿觸發 或 clr上升沿觸發
	if(clr)
		cnt <= 4'b0;
	else if(en) begin
		cnt <= cnt + 1'b1;
	end
	assign cnt_value = cnt;


endmodule

always@(posedge clk or posedge clr) 表示時序邏輯部分,posedge爲上升沿,negedge爲下降沿
注:同一個信號禁止同時出現上升與下降的觸發

Test Bench

用於對編寫好的moudle進行功能測試所編寫的邏輯,僅用在仿真驗證環節

`timescale 1ns/1ns
`define clk_period 10
module counter_tb;
	
	reg clk_t;
	reg en_t;
	reg clr_t;

	wire [7:0]cnt_value_t;


	counter counter_1(
		.clk(clk_t),
		.en(en_t),
		.clr(clr_t),
		.cnt_value(cnt_value_t[7:4])
	);

	counter counter_2(
		.clk(clk_t),
		.en(en_t),
		.clr(clr_t),
		.cnt_value(cnt_value_t[3:0])
	);

	initial clk_t = 1;	//初始化clk_t的值
	always #(`clk_period/2) clk_t = ~clk_t;
	//always表示循環
	initial begin
		en_t = 0;
		clr_t = 0;

		#(`clk_period*20)	//延時

		ent_t = 1;

		#(`clk_period*200)

		ent_t = 0;

		#(`clk_period*200)

		clr_t = 1;

		#(`clk_period*200)
		
		clr_t = 0;

		#(`clk_period*200)
		
		$stop;
	end
endmodule

timescale 第一個參數表示步進,第二個表示精度,一般寫成相同的
`define clk_period 10 重定義,與C語言相同
counter counter_1 類似C++中類的實例化,counter是類名,counter_1是實例化後的名稱

隨機數

myrand = {$random};	
myrand2 = {$random} % 65536;	// 0 - 65535
myrand3 = $random % 65536;		//-65535 - 65535

花括號類似絕對值

測試Task

task press_key
	begin
		...
	end
end task

類似C語言中的函數,press_key是函數名
調用的時候很簡單,如下:

always@(posedge press)
	press_key;

循環repeat

repeat(50) begin
	myrand = {$random} % 65536;
	#myrand key = ~key;
end

循環指定次數,例子中是循環50次

按鍵消抖

工程地址:
學習重點:
1、多模塊之間的連接
2、消抖原理
3、亞穩態處理

多模塊之間的連接

module key_cnt_led(
	led1,
	led2,
	led3,
	led4,
	key,
	clk,
	rst
);

input wire clk,rst;
input key;
reg key_1;
reg key_2;
output wire led1,led2,led3,led4;
wire outkey;

pushkey pushkey_01(
	.clk(clk),
	.key(key_2),
	.out(outkey)
);

count_led count_led_01(
	.led1(led1),
	.led2(led2),
	.led3(led3),
	.led4(led4),
	.clk(outkey),
	.rst(rst)
);

always@(posedge clk) begin
key_1 <= key;
key_2 <= key_1;
end
endmodule

就像用烙鐵焊接元器件一樣,使用wire將各個module連接起來

消抖原理

module pushkey(
	clk,
	key,
	out
);

input clk,key;
output reg out;

reg flag;
reg [20:0]cnt;

localparam PUSH = 1'b1;
localparam NO_PUSH = 1'b0;

always@(posedge clk) begin

if(!key) 
	flag <= PUSH;
else begin
	cnt[20:0] <= 21'd0;
	flag <= NO_PUSH;
end

case(flag)
	NO_PUSH:
		out <= 1'b0;
	PUSH:	if(cnt[20:0] == 21'd999999)
			out <= 1'b1;
		else
			cnt <= cnt + 21'b1;
endcase
end
endmodule

使用定時器對按鍵的狀態進行檢測,若20ms內沒有出現波動,則認爲按下了按鍵

亞穩態處理

亞穩態是由於異步系統信號的不確定性造成的。亞穩態的狀態會對次級系統造成嚴重的影響,導致系統崩潰
處理的辦法,使用級聯DQ觸發器即可解決

always@(posedge clk) begin
key_1 <= key;
key_2 <= key_1;
end

1

BCD數碼管

實現8個8位數碼管,根據分頻後的時鐘自動計數(16進制)

硬件結構

開發板:AC620
在這裏插入圖片描述

整體框架

在這裏插入圖片描述

BCDDisplay模塊

在這裏插入圖片描述
根據輸入的data每4位爲1組,共8組,對應8個數碼管,輪詢進行刷新

device_74hc595模塊

在這裏插入圖片描述
根據輸入的並行數據輸出串行數據,每次輸出後會產生鎖存脈衝(lock),在輸出過程中會產生busy高電平信號,等傳輸完畢後恢復低電平。

代碼部分

device_74hc595

module device_74hc595(
	clk,
	rst,
	data,
	busy,
	lock,
	lock595,
	out595,
	clk595
);

input clk,rst,lock;
input [15:0]data;
output reg lock595,clk595,busy,out595;

reg [4:0]send_cnt;
reg [15:0]data_buffer;

reg [1:0]flag;
localparam SEND_STATE = 2'b00;
localparam LOCK0_STATE = 2'b01;
localparam LOCK1_STATE = 2'b11;
localparam IDLE_STATE = 2'b10;

always@(posedge clk,negedge rst) 
if(!rst)begin
	send_cnt <= 5'b0;
	busy <= 1'b0;
	data_buffer <= 16'b0;
	flag <= IDLE_STATE;
	lock595 <= 1'b0;
	clk595 <= 1'b1;
	out595 <= 1'b0;
end
else 
	case(flag)
		SEND_STATE:
			if(clk595) begin
				if(send_cnt == 5'd16)
					flag <= LOCK0_STATE;
				else begin
					send_cnt <= send_cnt + 5'b1;
					out595 <= data_buffer[5'd15-send_cnt];
					clk595 <= 1'b0;
				end
			end
			else begin
				clk595 <= 1'b1;
			end
		LOCK0_STATE: begin
			lock595 <= 1'b1;
			flag <= LOCK1_STATE;
			end
		LOCK1_STATE: begin
			lock595 <= 1'b0;
			flag <= IDLE_STATE;
			end
		
		IDLE_STATE:
			if(lock)begin
				send_cnt <= 5'b0;
				data_buffer <= data;
				busy <= 1'b1;
				flag <= SEND_STATE;
			end
			else
				busy <= 1'b0;
		default:
			flag <= IDLE_STATE;
	endcase
endmodule

BCDdecoder

module BCDdecoder(
	data,
	out
);
input [3:0]data;
output reg [7:0]out;
always@(*)
case(data)
 4'h0:out = 7'b1000000;             
 4'h1:out = 7'b1111001;             
 4'h2:out = 7'b0100100;             
 4'h3:out = 7'b0110000;             
 4'h4:out = 7'b0011001;             
 4'h5:out = 7'b0010010;             
 4'h6:out = 7'b0000010;             
 4'h7:out = 7'b1111000;             
 4'h8:out = 7'b0000000;             
 4'h9:out = 7'b0010000;             
 4'ha:out = 7'b0001000;             
 4'hb:out = 7'b0000011;             
 4'hc:out = 7'b1000110;             
 4'hd:out = 7'b0100001;             
 4'he:out = 7'b0000110;             
 4'hf:out = 7'b0001110; 
endcase
  
endmodule

frequencydivider

module frequencydivider(
	clk,
	rst,
	fclk
);

input clk,rst;
output reg fclk;

reg [31:0]cnt;

always@(posedge clk,negedge rst)
if(!rst) begin
	cnt <= 0;
	fclk <= 0;
end
else if(cnt == 500) begin
	cnt <= 0;
	fclk <= ~fclk;
end
else
	cnt <= cnt + 1;
	
endmodule

BCDDisplay

module BCDDisplay(
	clk,
	rst,
	data,
	lock595,
	out595,
	clk595
);
wire fclk;
input clk,rst;
input [31:0]data;
output lock595,out595,clk595;
reg flag;
reg [7:0]selecter;
reg [7:0]srcdata;
wire [7:0]dstdata;
reg [31:0]data_buffer;
reg lock;
wire busy;
reg doonce;
reg [7:0]cnt;

localparam Wait_Busy = 1'b0;
localparam Circle_Play = 1'b1;

BCDdecoder BCDdecoder_01(
	.data(srcdata),
	.out(dstdata)
);

frequencydivider frequencydivider_01(
	.clk(clk),
	.rst(rst),
	.fclk(fclk)
);

device_74hc595 device_74hc595_01(
	.clk(fclk),
	.rst(rst),
	.data({dstdata,selecter}),
	.busy(busy),
	.lock(lock),
	.lock595(lock595),
	.out595(out595),
	.clk595(clk595)
);
always@(posedge clk,negedge rst)
if(!rst) begin
	selecter <= 8'b0000_0001;
	data_buffer <= 32'b0;
	flag <= Circle_Play;
	srcdata <= 8'b0;
	lock <= 0;
	cnt <= 8'd4;
	doonce <= 0;
end
else begin
	data_buffer <= data;
	case(flag)
		Circle_Play: begin
			srcdata <= data_buffer >> cnt;
			selecter <= {selecter[6:0],selecter[7]};
			flag <= Wait_Busy;
			doonce <= 0;
			
			if(cnt == 8'd28)
				cnt <= 8'b0;
			else
				cnt <= cnt + 4;
		end
		Wait_Busy:
			if(busy) begin
				lock <= 0;
				doonce <= 1;
			end
			else if(doonce)
				flag <= Circle_Play;
			else
				lock <= 1;
	endcase
end
endmodule

頂層代碼

module test_74hc595(
	clk,
	rst,
	key,
	lock595,
	out595,
	clk595
);
wire lock;
input clk;
input wire rst,key;
wire rst2;
reg [31:0]data;

output lock595,out595,clk595;

wire rst3;
wire clk2;

inkey inkey_1(
	.in(key),
	.out(lock),
	.clk(clk)
);

inkey inkey_2(
	.in(rst),
	.out(rst2),
	.clk(clk)
);


BCDDisplay BCDDisplay_1(
	.clk(clk),
	.rst(rst3),
	.data(data),
	.lock595(lock595),
	.out595(out595),
	.clk595(clk595)
);

frequencydivider frequencydivider2(
	.clk(clk),
	.rst(rst3),
	.fclk(clk2)
);


always @(negedge rst3,posedge clk2)
begin
if(!rst3)
	data <= 0;
else
	data <= data + 1;
end

assign rst3 = ~rst2;

endmodule
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章