verilog HDL入門


verilog HDL入門

特點

  • 類C語言
  • 並行執行
  • 硬件描述

設計流程: 自頂向下

前提:懂C語言和簡單的數電知識

簡單體驗

語法很類似C語言,同時不難看出描述的是一個多路選擇器

module muxtwo (out, a, b, s1);
    input a,b,s1;
    output out;
    reg out;
    always @(s1 or a or b)
        if(!s1) out = a;
    	else out = b;
endmodule

1580474384367

注意

  • 沒考慮時延問題
  • 沒有說明如果輸入a或b是三態的(高阻時)輸出應該是什么。

一 入門例子

例1.

多路選擇器

描述一個多路選擇器,控制信號sel,輸入信號in0、in1, 輸出信號out

module mux(out, in0, in1, sel);
    parameter N=8;
    
    output[N:1] out;
    input[N:1] in0, in1;
    input sel;
    
    assign out=sel?in1:in0;
endmodule

例2.

4位二進制加法計數器(帶同步清0)

1580475482543

進位輸出:當q為最大值(15)且cin=1時,cout=1;否則cout=0

module counter(q, cout, reset, cin, clk);
    parameter N=4;
    
    input reset, cin, clk;
    output cout, q;
    reg[N:1] q;	//寄存器型有保持功能
    
    //邏輯功能
    always @(posedge clk)	//時鍾上升沿執行
        begin
            if (reset) q<=0;
            else
                q<=q+cin
        end
                
     assign cout=&q && cin;	//&q是語法糖,相當於q[1]&...q[N]
endmodule

二 數據類型

空白符

忽略

注釋符

// 和 /**/
注釋最好是英文,EDA對中文不友好

數值

0/1/x/z
x : 不確定
z : 高阻態

整數

/*
format : +/-<size>'<base_format><number>
size的確定是一個好的編程習慣
*/

8'b1001101	//位寬為8位的二進制數
8'ha6		//位寬為8位的十六進制數
4'b1x_01	//下划線只是方便閱讀

//wrong eg.
4'd-4	//補碼表示 或 在位寬表達式前面加一個減號
3' b001	//空格
(4+4)'b11

//高級建模使用
integer idata;	//一個整型數據對象
//idata 位寬由機器和編譯器決定, 是有符號數
idata = -12;
idata[7:0] = 8'ha2

integer iram[7:0];	//數組

實數

/*
雖然parameter可以定義常量,parameter pi=3.14
但是verilog本身不能識別小數,不能直接運算小數(需要技巧)
*/

//小數點兩邊必須有數字
2.7
5.2e8	//520.0

//wrong eg.
6.
.3e5

//建模使用
real idata;


物理數據類型

物理數據類型:連線型、寄存器型和存儲器類型

標記符 名稱 類型
supply 電源級驅動 驅動
strong 強驅動 驅動
pull 上拉級驅動 驅動
large 大容性 存儲
weak 弱驅動 驅動
mediam 中性驅動 存儲
small 小容性 存儲
highz 高容性 高阻

信號強度:表示數字電路中不同強度的驅動源,解決不同驅動強度存在下的賦值沖突。

簡單理解就是,表格里的驅動源,從上到下強度依次減弱,supply相當於短路,highz相當於斷路。

連線型

連線型數據類型 功能說明
wire, tri 標准連續(缺省默認類型)
tri1 上拉電阻
tri0 下拉電阻
supply1 電源線,高電平1
supply0 電源線,低電平0
... ...

寄存器型

與連線型數據的區別:reg型數據保存最后一次賦值,而wire型數據需要持續的驅動。reg默認初始值為不定值x, 且默認無符號。

//有符號寫法
reg signed[3:0] rega;
rega = -2;	//rega值為1110(14)

存儲器類型

可以理解為寄存器的組合(二維寄存器)

reg[7:0] memory[63:0];	//寬度為8,深度64
memory[0] = 8'h12;
memory = 12;	//error

向量與標量

//vector 默認
wire vectored[7:0] v_data;
assign a = v_data[0];
assign b = v_data[5:3];
assign v_data[1] = 1'b1;

//scalar
wire scalared[7:0] s_data;
assign a = v_data[0];	//error

字符串

字符串變量其實就是寄存器變量,由""括起。

module string_test;
    reg[19*8:1] str;
    
	initial
	begin
		str = "I love verilog HDL!";
		$display("str is :%H",str);
	end
endmodule

時間型數據對象

time idata;
time iram[7:0];

參數

parameter H=1;
defparam H=2;
//僅限於當前模塊的參數定義
localparam real PI = 3.14;	

三 操作符

賦值

= 與 <= : https://www.cnblogs.com/friedCoder/articles/12257385.html

等式

邏輯相等‘’與邏輯全等‘=’

(1)、邏輯相等:兩個操作數逐位比較,如果兩個進行比較的位是不定態‘x’或者高阻態’z’,則輸出x

$displayb ( 4’b0011 == 4’b1010 ); // 0
$displayb ( 4’b0011 != 4’b1x10 ); // 1
$displayb ( 4’b1010 == 4’b1x10 ); // x
$displayb ( 4’b1x10 == 4’b1x10 ); // x
$displayb ( 4’b1z10 == 4’b1z10 ); // x

(2)、邏輯全等

$displayb ( 4’b01zx === 4’b01zx ); // 1
$displayb ( 4’b01zx !== 4’b01zx ); // 0
$displayb ( 4’b01zx === 4’b00zx ); // 0
$displayb ( 4’b01zx !== 4’b11zx ); // 1

在進行全等運算時,對不定態與高阻態也要進行比較,當兩個操作數完全一致時,其結果才為1,否則為0

比較

對於<、>之類的操作符,操作數中只要有一位為x或z,結果都為x

拼接

起拼接的作用 如 a = {b[5],b[4:0]}
意思為 b的最高位和b的低五位拼接起來,組成的a為6位

縮位

~^ : 位取同或操作

四 順序語句

//下面2個並行賦值
assign p_sum_one = data1 + data2;
assign p_sum_two = data1 + data3;

always @(data1, data2, data3);
begin
    //下面2個順序賦值
    assign s_sum_one = data1 + data2;
    assign s_sum_one = data1 + data2;
end

//但是實際生成電路后,會發現這4個加法器是並行執行

if 條件語句

if 條件
    順序語句1;
elsif 條件
    順序語句2;
end if

case 條件語句

//要求匹配完全一致,包括x和z

casez語句:忽略值為z的位

casex語句:忽略值為z或x位

repeat語句

repeat(8)
begin
	順序語句;
end

forever

永遠執行

五 自定義原語 UDP

原語只能有一個標量輸出(0,1或x),可以有多個標量輸入(0,1,z或x,這里x和z同等看待)。

六 任務和函數

verilog的任務 = C語言的函數

task cal_num_one;
    input[15:0] data;
    output[4:0] num;
    
    integer i, j;
    begin
        i = 0;
        for(j=0; j<16; j++) 
            if(data[j])
                i = i + 1;
        
        num = i[4:0];
    end
endtask

若在函數定義中沒有指定函數值得取值范圍和類型,則函數默認返回1位二進制數。返回值的類型可以是real,integer,time或者realtime之一。通過關鍵詞signed可以把返回值聲明為帶符號值。

function [automatic] [signed]
         [range or type] function_id;
input_declaration
other_declaration
 
statements
endfunction


//eg.
    function [7:0] expected_led;
       input [7:0] swt;
    begin      
       expected_led[0] = ~swt[0];
       expected_led[1] = swt[1] & ~swt[2];
       expected_led[3] = swt[2] & swt[3];
       expected_led[2] = expected_led[1] | expected_led[3];
       expected_led[7:4] = swt[7:4];
    end   
    endfunction  

任務調用

端口連接信號的順序必須與任務定義時的端口順序一致。由於任務內的語句是順序執行,所以輸出信號的類型必須是寄存器類型。

函數

函數只能返回一個信號,即只有一個輸出信號(函數名就是輸出信號);且函數不能調用任務;也不能帶時序控制。

系統任務函數

七 其它語法

阻塞與非阻塞

  1. 兩者都能實現組合電路建模
  2. 如果always進程中的敏感信號列表包括了所有賦值操作符右邊的信號,則兩者沒區別。
  3. 如果always進程中的敏感信號列表沒有包括所有賦值操作符右邊的信號,則兩者有差別,至少在ModelSim中的仿真有差別。
  4. always進程中的敏感信號列表沒有包括所有賦值操作符右邊的信號時,多條阻塞賦值語句書寫順序是有先后講究的。
  5. 因此,推薦組合電路使用阻塞賦值,並且always敏感列表包括所有賦值操作符右邊的信號,即使用always@(*)。時序電路推薦非阻塞語句。
//3個寄存器
always@(posedge clk)
begin
	temp_one <= a;
    temp_two <= temp_one;
    y_r <= temp_two;
end

//1個寄存器
always@(posedge clk)
begin
	temp_one = a;
    temp_two = temp_one;
    y_r = temp_two;	//最后相當於y_r = a
end

//3個寄存器
always@(posedge clk)
begin
    y_r = temp_two;
    temp_two = temp_one;
	temp_one = a;
end

預編譯指令

類似`define

時延

assign #2 a = b; //b計算好后,延時2個時間單位送給a

事件

上升沿,下降沿;電平事件



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM