0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Verilog常用基礎(chǔ)語法全梳理

ZYNQ ? 來源:ZYNQ ? 作者:ZYNQ ? 2022-11-10 10:05 ? 次閱讀

	

把前面看過的夏宇聞老師的那本書上面的verilog的常用語法進(jìn)行了一下梳理,并且為了熟悉語法在HDLBits刷了刷題,一些例子就直接從那個(gè)里面引用了。參考過的內(nèi)容列在下面:

·verilog數(shù)字系統(tǒng)設(shè)計(jì)教程(夏宇聞):學(xué)習(xí)verilog的必讀書。第四版的第三章到第七章,夏宇聞老師這本書上講的比較詳細(xì)。

·HDLBits:類似于CS的LeetCode,題目倒是沒有LeetCode多,適合入門語法。

·Verilog Tutorial : 這個(gè)里面對(duì)verilog語法講的很細(xì),而且有大量的代碼示例,不過代碼不能直接復(fù)制下來運(yùn)行,但是用一些小trick還是可以把代碼搞下來的。

01verilog模塊的結(jié)構(gòu)

verilog語法中最基本的元素就是模塊了,主要包括模塊聲明以及模塊內(nèi)容。首先是模塊的聲明,具體的聲明結(jié)構(gòu)如下

module Hello_World(<端口信號(hào)列表>); //注意這里要有分號(hào)
  <邏輯代碼>
endmodule

接下來是模塊內(nèi)容,主要包括I/O口的說明,內(nèi)部信號(hào)的說明,功能的定義三個(gè)結(jié)構(gòu)。

(1)I/O口的說明

輸入端口:
  input[信號(hào)位寬-1:0] 端口1;
        input[信號(hào)位寬-1:0] 端口2;
        ...
輸出端口:
  output[信號(hào)位寬-1:0] 端口1;
        output[信號(hào)位寬-1:0] 端口2;
        ...
輸入/輸出端口:
  inout[信號(hào)位寬-1:0] 端口1;
        inout[信號(hào)位寬-1:0] 端口2;
        ...

(2)內(nèi)部信號(hào)的說明

reg [width-1:0] 變量1,變量2,...;
wire[width-1:0]變量1,變量2,...;

(3)功能定義

主要有三種最基本的功能定義方法,分別是always,assign,initial。一個(gè)module里面可以寫多個(gè)always,assign,initial,這些功能在電路通電之后也是同時(shí)開始執(zhí)行的。

·initial用在仿真。主要有以下兩種功能,一方面可以在仿真開始時(shí)對(duì)各變量進(jìn)行初始化,這個(gè)初始化過程不需要任何仿真時(shí)間,即在 0 ns 時(shí)間內(nèi),便可以完成初始化工作;另一方面可以用來生成激勵(lì)波形作為電路的測試仿真信號(hào)。

// 完成初始化
initial begin
  areg = 0;    //初始化一個(gè)寄存器
  for(index=0;indexindex=index+1)
    memory[index] = 0; //初始化一個(gè)memory
end
// 完成激勵(lì)波形的生成
initial begin
  inputs = 8'b0000_0000;
  #10 inputs = 8'b0110_0101;
  #10 inputs = 8'b1111_0101;
end

·assign用于實(shí)現(xiàn)組合邏輯,直接互聯(lián)不同的信號(hào),或者變量值。

assign  = <變量或者常量>;
// 右邊也可以使用 (判斷條件)?(判斷條件為真時(shí)的邏輯處理):(判斷條件為假時(shí)的邏輯處理)
// 完成更加簡潔的if...else語句

·always語句可以實(shí)現(xiàn)組合邏輯,也可以實(shí)現(xiàn)時(shí)序邏輯。它是一直運(yùn)行的,但是后面跟著的過程塊是否執(zhí)行,就要看它的觸發(fā)條件是否滿足了,這個(gè)觸發(fā)條件就寫在括號(hào)中??梢杂尚盘?hào)的邊沿觸發(fā),也可以由信號(hào)的變化觸發(fā)。

// 所有信號(hào)變換都可以觸發(fā),是組合邏輯
always@(*) begin
  ...
end
// 信號(hào)a與信號(hào)b的變化都可以觸發(fā),是組合邏輯
always@(a or b) begin
  ...
end
// 時(shí)鐘的上升沿或者復(fù)位信號(hào)的下降沿可以觸發(fā),是時(shí)序邏輯
always@(posedge clk or negedge rst_n) begin
  ...
end

(4)引用模塊

在引用時(shí)既可以按照模塊定義的端口順序來連接,不用標(biāo)明原模塊定義時(shí)規(guī)定的端口名,也可以在引用時(shí)采用“.”符號(hào),這樣就不用完全按照模塊定義的端口順序來連接了,一般還是使用“.”來引用比較好,這樣邏輯比較清晰。

...
MyDesign M1(.sin(in),.pout(out));
...

MyDesign 是已經(jīng)定義的另一個(gè)模塊,M1是自己取的一個(gè)名字,sin與pout是MyDesign模塊里面端口的名字,in與out是在正在寫的模塊中定義的兩個(gè)信號(hào),這兩個(gè)信號(hào)分別連接到sin與pout端口。

02數(shù)據(jù)類型

數(shù)據(jù)主要可以分為兩種類型,一種是常量類型的數(shù)據(jù),另一種是變量類型的數(shù)據(jù)。

(1)常量類型的數(shù)據(jù)

·整數(shù):二進(jìn)制(b),十進(jìn)制(d),十六進(jìn)制(h)。

·x和z值:

·x代表未定值:不確定狀態(tài)為高還是為低,旨在告訴綜合工具設(shè)計(jì)者不關(guān)心它的電平是多少,是0是1都可以。

·z代表高阻值:輸出引腳與內(nèi)部電路斷開,不導(dǎo)通,其電平隨外部電平高低而定,表示設(shè)計(jì)者不驅(qū)動(dòng)這個(gè)信號(hào)。

·一個(gè)x可以用來定義十六進(jìn)制數(shù)的4位二進(jìn)制數(shù)的狀態(tài)或者二進(jìn)制數(shù)的1位。建議在寫case語句中使用這些值。

·負(fù)數(shù):在位寬表達(dá)式前加一個(gè)減號(hào)就可以定義一個(gè)負(fù)數(shù)。

·下劃線:主要用來分隔開數(shù)的表達(dá)式以提高程序的可讀性,它只能用在具體的數(shù)字之間。

<位寬><進(jìn)制><數(shù)字>
8'b10101100 // 位寬為8的數(shù)的二進(jìn)制表示,'b表示二進(jìn)制
8'ha2 // 位寬為8的數(shù)的十六進(jìn)制表示,'h表示十六進(jìn)制
<數(shù)字>
10 //默認(rèn)采用十進(jìn)制,并且數(shù)字的位寬采用默認(rèn)位寬(與機(jī)器系統(tǒng)相關(guān),最少32位)


4'b10x0  //位寬為4的二進(jìn)制數(shù),從低位數(shù)起第2位是不定值
8'h4x    //位寬為8的二進(jìn)制數(shù),其低四位值為不定值


-8'd5//這個(gè)表達(dá)式代表5的補(bǔ)數(shù)


16'b1010_1011_1111_1010

·參數(shù):使用parameter來定義常量,注意在引用實(shí)例的時(shí)候可以通過參數(shù)的傳遞來改變定義時(shí)規(guī)定的參數(shù)。

//parameter可以直接定義一系列常量參數(shù)
parameter a=2,b=2.3,c=a-1;
//可以通過傳參修改module中定義的parameter
module Decode(A,B);
  parameter width=1,polarity=1;
  ...
endmodule
module Decode_top;
  wire[3:0] A1;
  wire[3:0] B1;
  wire[3:0] A2;
  wire[3:0] B2;
  Decode #(4,0) D1(A1,B1); // D1實(shí)例引用的width=4,polarity=0
  Decode #(5) D2(A2,B2);   // D2實(shí)例引用的width=5,polarity=1
endmodule

(2)變量類型的數(shù)據(jù)

·wire型:常用來表示用以assign關(guān)鍵字指定的組合邏輯信號(hào)。verilog程序模塊中輸入、輸出信號(hào)類型默認(rèn)時(shí)自動(dòng)定義為wire型。wire型信號(hào)可以用做任何方程式的輸入,也可以用作assign語句的輸出。

·reg型:常用來表示always模塊內(nèi)的指定信號(hào),在always模塊內(nèi)被賦值的每一個(gè)信號(hào)都必須定義成reg型。reg的默認(rèn)初始值是不定值。

·memory型:從編程角度可以理解成一個(gè)多維數(shù)組,從物理角度可以理解成RAM型存儲(chǔ)器或者ROM存儲(chǔ)器,從實(shí)現(xiàn)角度可以理解成是reg型數(shù)據(jù)的擴(kuò)展。

wire [3:0] a,b; //定義了兩個(gè)4位的wire型數(shù)據(jù)
reg [3:0] a,b;  //定義了兩個(gè)4位的reg型數(shù)據(jù)
reg [n-1:0] memory [m-1:0] //[n-1:0]定義存儲(chǔ)器中每一個(gè)存儲(chǔ)單元的大小,[m-1:0]定義了
         //存儲(chǔ)器中存儲(chǔ)單元的個(gè)數(shù)也可以理解成各存儲(chǔ)單元的地址
memory[3] = 0; //注意對(duì)memory中的存儲(chǔ)單元進(jìn)行讀寫操作的時(shí)候必須指定該單元在存儲(chǔ)器中的地址

03運(yùn)算符及表達(dá)式

·算術(shù)運(yùn)算符

+  //加
-  //減
*  //乘
/  //除 
%  //求模

進(jìn)行整數(shù)除法運(yùn)算時(shí)結(jié)果值略去小數(shù)部分,只留下整數(shù)部分,取模運(yùn)算要求%兩側(cè)均為整數(shù)。

·位運(yùn)算符

~  //取反
&  //按位與
|  //按位或
^  //按位異或
~^ //按位同或

不同長度的數(shù)據(jù)在進(jìn)行位運(yùn)算的時(shí)候,系統(tǒng)會(huì)自動(dòng)地將兩者按右端對(duì)齊,位數(shù)少的操作數(shù)會(huì)在相應(yīng)的高位用0填滿。

·邏輯運(yùn)算符

&& //邏輯與
|| //邏輯或
!  //邏輯非

·關(guān)系運(yùn)算符

a//小于
a>b   //大于
a<=b  //小于等于
a>=b  //大于等于

·等式運(yùn)算符

==  //等于
!=  //不等于

·移位運(yùn)算符

a>>n  //a右移n位
a<//a左移n位

兩種移位運(yùn)算都用0來填補(bǔ)移出的空位。

·位拼接運(yùn)算符

{信號(hào)1的某幾位,信號(hào)2的某幾位,...}
{a,b[3:0],w,3'b101}
{4{w}} //位拼接可以用來簡化表達(dá)式,這樣寫等同于{w,w,w,w}
{b,{3{a,b}}} //這樣寫等同于 {b,a,b,a,b,a,b}

位拼接表達(dá)式不允許存在沒有指明位數(shù)的信號(hào)。

·縮減運(yùn)算符

reg [3:0] B;
reg C;
C = &B; // 相當(dāng)于 C=((B[0] & B[1])&B[2])&B[3]

與之前的位運(yùn)算不同,縮減運(yùn)算是對(duì)單個(gè)操作數(shù)各位的邏輯操作,最終的運(yùn)算結(jié)果是1位二進(jìn)制數(shù)。

04條件語句及循環(huán)語句

(1)條件語句

條件語句主要有兩種寫法,一種是if else的寫法,另一種是case的寫法。

// if else 的寫法
if(a==2'b00) begin
  <具體邏輯>
end
else if(a==2'b01) begin
  <具體邏輯>
end
else begin
  <具體邏輯>
end
// case 的寫法
case(a)
  2'b00:<具體邏輯>
  2'b01:<具體邏輯>
  default:<具體邏輯>
endcase

還有用法與case類似的casex與casez,這兩者可以用來處理比較過程中不必考慮的情況。其中casez語句用來處理不必考慮高阻值z的比較過程,casex語句則將高阻值z和不定值都視為不必關(guān)心的情況。所謂不必關(guān)心的情況就是說,在表達(dá)式進(jìn)行比較時(shí)不將該位的狀態(tài)考慮在內(nèi)。

(2)循環(huán)語句

總體來說循環(huán)語句用到的不是很多,這里就列舉兩個(gè)最常用的forever和for循環(huán)。

·forever循環(huán)語句常用來產(chǎn)生周期性的波形,用來作為仿真信號(hào),必須要寫在initial塊中。

·for循環(huán)語句的一般形式是for(<變量名>=<初值>;<判斷表達(dá)式>;<變量名>=<新值>)。for可以綜合的,for幾次就相當(dāng)于把你的電路復(fù)制幾次。

initial begin
   clk = 0;
   forever begin
       clk = ~clk;
       #5;
   end
end


initial begin
    counter2 = 'b0 ;
    for (i=0; i<=10; i=i+1) begin
        #10 ;
        counter2 = counter2 + 1'b1 ;
    end
end

05塊語句

·順序塊:采用begin end語句,塊內(nèi)的語句是按照順序執(zhí)行的,前面的語句執(zhí)行完才輪到后面的語句執(zhí)行,每條語句的延遲時(shí)間是相對(duì)于前一條語句的仿真時(shí)間定的。

·并行塊:采用fork join語句,塊內(nèi)的語句是并行執(zhí)行的,每條語句的延遲時(shí)間是相對(duì)于程序進(jìn)入塊內(nèi)的時(shí)間。并行塊是不可綜合的,只能用在仿真中。

module begin_tb();
  reg [7:0] r;
regclk;


  initial begin:begin_tb //這里可以將塊名寫上
     clk = 0;
     #50 r = 8'h35;
     #30 r = 8'h12;
     #40 r = 8'h41;
  end
  always #5 clk = ~clk;
endmodule


module fork_tb();
  reg [7:0] r;
  reg clk;


  initial fork:fork_tb //這里可以將塊名寫上
     clk = 0;
     #50 r = 8'h35;
     #30 r = 8'h12;
     #40 r = 8'h41;
  join
  always #5 clk = ~clk;
endmodule

00a27846-6090-11ed-8abf-dac502259ad0.png

00bc1c60-6090-11ed-8abf-dac502259ad0.png

上面是begin end的仿真時(shí)序圖,下面是fork join的仿真時(shí)序圖。從仿真時(shí)序圖中可以明顯的看到,begin end是串行的時(shí)序而fork join是并行的時(shí)序。

·生成塊:采用generate endgenerate語句,這一聲明語句方便參數(shù)化模塊的生成。當(dāng)對(duì)矢量中的多個(gè)位進(jìn)行重復(fù)操作時(shí),或者當(dāng)進(jìn)行多個(gè)模塊的實(shí)例引用的重復(fù)操作時(shí),或者在根據(jù)參數(shù)的定義來確定程序中是都應(yīng)該包括某段verilog代碼的時(shí)候,使用生成語句能夠大大簡化程序的編寫過程。

1、for:在generate中的應(yīng)用主要是用來減少重復(fù)操作。

//-------------module-------------//
// 頂層模塊包含N個(gè)半加器
module top(a,b,sum,cout);
  parameter N=2;
  input  [N-1:0] a, b;
  output  [N-1:0] sum, cout;


  // 聲明一個(gè)暫時(shí)的循環(huán)變量
  genvar i;


  // 生成N次
  generate
    for (i = 0; i < N; i = i + 1) begin
          half_add u0(a[i], b[i], sum[i], cout[i]);
    end
  endgenerate
endmodule


// 定義一個(gè)半加器
module half_add(a, b, sum, cout);
  input a,b;
  output sum,cout;


  assign sum  = a ^ b;
  assign cout = a & b;
endmodule
//-------------testbench-------------//
`timescale 1ns / 1ns
module top_tb;
  parameter N = 2;
  reg  [N-1:0] a, b;
  wire [N-1:0] sum, cout;


  top instance1( .a(a), .b(b), .sum(sum), .cout(cout));


  initial begin
    a <= 0;
b<=0;


    #10 a <= 'h2;
      b <= 'h3;
    #20 b <= 'h4;
    #10 a <= 'h5;
  end
endmodule

00c86ace-6090-11ed-8abf-dac502259ad0.jpg

00dc3ad6-6090-11ed-8abf-dac502259ad0.jpg

具體生成的RTL圖以及仿真結(jié)果如上圖所示。

2、if:在generate中主要用來根據(jù)參數(shù)的定義來確定程序。

//-------------module-------------//
module top(a, b, sel, out);
  input a,b,sel;
  output out;
  parameter USE_CASE = 0;
  // 使用 generate 塊選擇使用 mux_case 或者 mux_assign
  generate
    if (USE_CASE)
      mux_case m1 (.a(a), .b(b), .sel(sel), .out(out));
    else
      mux_assign m2 (.a(a), .b(b), .sel(sel), .out(out));
  endgenerate


endmodule
// Design #1: 使用assign
module mux_assign ( input a, b, sel,
                   output out);
  assign out = sel ? a : b;
  // 這個(gè)display可以在仿真時(shí)標(biāo)明在用哪一個(gè)design
  initial
    $display ("mux_assign is instantiated");
endmodule




// Design #2: 使用case
module mux_case (input a, b, sel,
                 output reg out);
  always @ (a or b or sel) begin
    case (sel)
      0 : out = a;
        1 : out = b;
    endcase
  end
  // 這個(gè)display可以在仿真時(shí)標(biāo)明在用哪一個(gè)design
  initial
    $display ("mux_case is instantiated");
endmodule
//-------------testbench-------------//
`timescale 1ns / 1ns
module top_tb;
  reg a, b, sel;
  wire out;
  integer i;
  // 使用 USE_CASE 選擇用哪個(gè) design
  initial 
  $display("USE_CASE = %0d",0);
  top #(.USE_CASE(0)) u0 ( .a(a), .b(b), .sel(sel), .out(out));


  initial begin
    a <= 0;
    b <= 0;
    sel <= 0;


    for (i = 0; i <= 2; i = i + 1) begin
      #10 a <= $random;
          b <= $random;
          sel <= $random;
      $display ("i=%0d a=0x%0h b=0x%0h sel=0x%0h out=0x%0h", i, a, b, sel, out);
    end
  end
endmodule

00f61500-6090-11ed-8abf-dac502259ad0.jpg

上面是仿真結(jié)果在tcl的輸出,可以看到使用不同的USE_CASE可以選取不同的design。

3、case:在generate中主要用來根據(jù)參數(shù)的定義來確定程序。

//-------------module-------------//
module top (a, b, cin, sum, cout);
  input a,b,cin;
  output sum,cout;
  
  parameter ADDER_TYPE = 1;


  generate
    case(ADDER_TYPE)
      0 : ha u0 (.a(a), .b(b), .sum(sum), .cout(cout));
      1 : fa u1 (.a(a), .b(b), .cin(cin), .sum(sum), .cout(cout));
    endcase
  endgenerate
endmodule
// Design #1: 半加器
module ha (input a, b,
           output reg sum, cout);
  always @ (a or b)
  {cout, sum} = a + b;


  initial
    $display ("Half adder instantiation");
endmodule


// Design #2: 全加器
module fa (input a, b, cin,
           output reg sum, cout);
  always @ (a or b or cin)
  {cout, sum} = a + b + cin;


    initial
      $display ("Full adder instantiation");
endmodule


//-------------testbench-------------//
`timescale 1ns / 1ns
module top_tb;
  reg a, b, cin;
  wire sum, cout;


  initial 
  $display("ADDER_TYPE = %0d",0);
  top #(.ADDER_TYPE(0)) u0 (.a(a), .b(b), .cin(cin), .sum(sum), .cout(cout));


  initial begin
    a <= 0;
    b <= 0;
    cin <= 0;


    $monitor("a=0x%0h b=0x%0h cin=0x%0h cout=0%0h sum=0x%0h",
             a, b, cin, cout, sum);


    for (int i = 0; i<=2; i = i + 1) begin
    #10 a <= $random;
    b <= $random;
    cin <= $random;
    end
  end
endmodule

011cf864-6090-11ed-8abf-dac502259ad0.jpg

上面是仿真結(jié)果在tcl的輸出,可以看到使用不同的ADDER_TYPE可以選取不同的design。

06任務(wù)

任務(wù)要寫在task endtask中,有點(diǎn)像matlab中函數(shù)這個(gè)概念,其中可以有input、output、inout端口作為出入口參數(shù)。注意任務(wù)中不能出現(xiàn)initial語句和always語句, 但任務(wù)調(diào)用語句可以在initial語句和always語句中使用(這些都是對(duì)static task來說的,automatic task略有不同,一般默認(rèn)是static task)。

module top_tb;


reg [7:0] x, y, z;


 task sum;
  input  [7:0] a, b;
  output [7:0] c;
  begin
    c = a + b;
  end
endtask




initial begin:test
    {x,y} = {8'd2,8'd3}; 
    sum (x, y, z);
    #10 {x,y} = {8'd5,8'd1};
    sum (x, y, z);
end


endmodule

x 和 y 是輸入值,最后計(jì)算結(jié)果存儲(chǔ)在 z 中。

012e1d56-6090-11ed-8abf-dac502259ad0.jpg

07常用的系統(tǒng)任務(wù)

(1)格式化輸出函數(shù)

·$display

reg [3:0] rval;
initial begin
    rval = 3;
    $display("rval = %h hex, rval = %d decimal, rval = %b binary", rval, rval, rval);
    $display("current scope is %m"); // 輸出等級(jí)層次的名字
    $display("%s", "Hello World");   
     //在%和表示輸出格式的字符之間插入一個(gè)0自動(dòng)調(diào)整顯示輸出的數(shù)據(jù)寬度  
    $display("Simulation time is %0d", $time);
end

013c1e92-6090-11ed-8abf-dac502259ad0.jpg

·$strobe

與display不同,它可以確保所有在同一時(shí)鐘沿賦值的其它語句在執(zhí)行完畢之后才顯示數(shù)據(jù)。

reg [3:0] a,b;
wire [4:0] y = a + b;
// 采用display的方式輸出
initial begin
    a = 3;
    b = 2;
    $display("$display: time =%0d, a= %d, b=%d, y= %d ",$time ,a, b, y);
    #10
    a = 4;
    $display("$display: time =%0d, a= %d, b=%d, y= %d ",$time,a, b, y);
    b = 5;
    $display("$display: time =%0d, a= %d, b=%d, y= %d ",$time ,a, b, y);
    #10;
end
// 采用strobe的方式輸出
initial begin
    a = 3;
    b = 2;
    $strobe("$strobe: time =%0d, a= %d, b=%d, y= %d ",$time ,a, b, y);
    #10
    a = 4;
    $strobe("$strobe: time =%0d, a= %d, b=%d, y= %d ",$time,a, b, y);
    b = 5;
    $strobe("$strobe: time =%0d, a= %d, b=%d, y= %d ",$time ,a, b, y);
    #10;
end

015573ce-6090-11ed-8abf-dac502259ad0.png

從上面演示的例子可以看出,當(dāng)打印輸出連續(xù)賦值語句或module例化的輸出結(jié)果時(shí),在當(dāng)前的時(shí)間點(diǎn)內(nèi),$display無法輸出當(dāng)前運(yùn)算結(jié)果,但 $strobe卻可以顯示組合邏輯結(jié)果。因此, 如果希望在當(dāng)前時(shí)間點(diǎn)內(nèi)打印連續(xù)賦值語句或module例化的輸出結(jié)果推薦使用$strobe, 而其他情況下使用$display可以顯示更多的細(xì)節(jié)。

·$monitor

一般在initial塊中調(diào)用,可以不間斷地對(duì)所設(shè)定的信號(hào)進(jìn)行監(jiān)視。一般與時(shí)間度量系統(tǒng)函數(shù)$time一起使用,具體的應(yīng)用實(shí)例在下面時(shí)間度量系統(tǒng)函數(shù)給出。

(2)時(shí)間度量系統(tǒng)函數(shù)

·$time

該函數(shù)可以返回一個(gè)64位整數(shù)來表示當(dāng)前的仿真時(shí)刻,該時(shí)刻受時(shí)間尺度比例的影響,例如在下面的例子中,時(shí)間尺度是10ns,16ns與32ns就要變成1.6與3.2,同時(shí)因?yàn)?time的輸出要是整數(shù),因此實(shí)際的輸出為2和3。

`timescale 10ns / 1ns
module top_tb ();
 
reg set;
initial begin
  $monitor("time=%0d",$time," ","set=",set);
  #1.6 set = 0;
  #1.6 set = 1;
end
endmodule

016b0b80-6090-11ed-8abf-dac502259ad0.png

(3)仿真暫停與退出函數(shù)

·$finish

該系統(tǒng)函數(shù)可以退出仿真器,返回主操作系統(tǒng)結(jié)束仿真過程,并且可以輸出當(dāng)前的仿真時(shí)刻和位置。

01777d98-6090-11ed-8abf-dac502259ad0.png

01857baa-6090-11ed-8abf-dac502259ad0.png

·$stop

該系統(tǒng)函數(shù)暫停模擬并將模擬器置于交互模式。

(4)文件讀取到寄存器函數(shù)

·readmemh

$readmemh("<數(shù)據(jù)文件名>",<存儲(chǔ)器名>);
$readmemh("<數(shù)據(jù)文件名>",<存儲(chǔ)器名>,<起始地址>);
$readmemh("<數(shù)據(jù)文件名>",<存儲(chǔ)器名>,<起始地址>,<終止地址>);

·<數(shù)據(jù)文件名> 是指向一個(gè)文本文件,用來保存仿真的數(shù)據(jù)。每一行代表一個(gè)十六進(jìn)制的數(shù)據(jù)。

·<存儲(chǔ)器名> 為仿真文件中例化的存儲(chǔ)器的名稱。

·<起始地址>,<終止地址> 指示將文本文件中的數(shù)據(jù)存儲(chǔ)到存儲(chǔ)器的位置段。

`timescale 1ns / 1ps
module top_tb();   
reg clk = 0;
always clk = #10 ~clk;
reg [7:0] ram[0:127];
localparam FILE_NAME = "../../../led_sim.sim";
initial begin
    $readmemh (FILE_NAME, ram, 2, 8); //從第二個(gè)16進(jìn)制數(shù)據(jù)讀起,但是由于ram是8位的因此每次存入8
end
integer i;
initial
begin
    #20;
    for(i = 0; i < 16; i = i + 1)
        $display("ram[%02d] = 0x%h ", i, ram[ i ] );
    #8000;
    $stop;
end
    
endmodule

01bb73d6-6090-11ed-8abf-dac502259ad0.jpg

上圖左邊是文件中的數(shù)據(jù),右邊是仿真中的輸出值,可以看到ram[00],ram[01],ram[09]-ram[15]的值都是0xxx(未初始化的值),真正初始化的只有ram[02]- ram[08]。

(5)隨機(jī)數(shù)生成函數(shù)

·$random

一般用法是$random%b,其中b>0,它給出了一個(gè)范圍在(-b+1):(b-1)中的隨機(jī)數(shù)。也可以通過位拼接操作{$random}%b,生成0:(b-1)之間的隨機(jī)數(shù)。

reg [23:0] rand;
rand = $random%60; // 生成一個(gè)范圍在 [-59,59] 之間的隨機(jī)數(shù)
rand = {$random}%60;// 生成一個(gè)范圍在 [0,59] 之間的隨機(jī)數(shù)

08編譯預(yù)處理

·宏定義 `define

`define WORDSIZE 8
module
reg [`WORDSIZE-1:0] data; //相當(dāng)于定義[7:0],注意在引用時(shí)也要加`
...
endmodule

·文件包含 `include

`include相當(dāng)于將被引用的文件全部囊括進(jìn)引用文件中??梢詫⒁恍┏S玫暮甓x命令或任務(wù)(task)組成一個(gè)文件,然后用`include命令將這些宏定義包含到自己所寫的源文件中。

·時(shí)間尺度 `timescale

該命令的格式如下:`timescale<時(shí)間單位>/<時(shí)間精度>,時(shí)間單位參量用來定義模塊中仿真時(shí)間和延遲時(shí)間的基準(zhǔn)單位,時(shí)間精度參量用來定義該模塊仿真時(shí)間的精確程度。

`timescale 10ns/1ns
parameter d = 1.55;
#d //時(shí)間單位為10ns,時(shí)間精度為1ns,1.55*10=15.5,再根據(jù)精度得到16,因此#d實(shí)際上是延時(shí)16ns

審核編輯 :李倩


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 模塊
    +關(guān)注

    關(guān)注

    7

    文章

    2658

    瀏覽量

    47294
  • 仿真
    +關(guān)注

    關(guān)注

    50

    文章

    4023

    瀏覽量

    133338
  • Verilog
    +關(guān)注

    關(guān)注

    28

    文章

    1343

    瀏覽量

    109926

原文標(biāo)題:Verilog常用基礎(chǔ)語法全梳理

文章出處:【微信號(hào):ZYNQ,微信公眾號(hào):ZYNQ】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    Verilog硬件描述語言參考手冊

    一. 關(guān)于 IEEE 1364 標(biāo)準(zhǔn)二. Verilog簡介三. 語法總結(jié)四. 編寫Verilog HDL源代碼的標(biāo)準(zhǔn)五. 設(shè)計(jì)流程
    發(fā)表于 11-04 10:12 ?0次下載

    Verilog語法中運(yùn)算符的用法

    verilog語法中使用以下兩個(gè)運(yùn)算符可以簡化我們的位選擇代碼。
    的頭像 發(fā)表于 10-25 15:17 ?219次閱讀
    <b class='flag-5'>Verilog</b><b class='flag-5'>語法</b>中運(yùn)算符的用法

    Verilog HDL的基礎(chǔ)知識(shí)

    本文繼續(xù)介紹Verilog HDL基礎(chǔ)知識(shí),重點(diǎn)介紹賦值語句、阻塞與非阻塞、循環(huán)語句、同步與異步、函數(shù)與任務(wù)語法知識(shí)。
    的頭像 發(fā)表于 10-24 15:00 ?142次閱讀
    <b class='flag-5'>Verilog</b> HDL的基礎(chǔ)知識(shí)

    TestStand表達(dá)式中常用語法規(guī)則和運(yùn)算符使用

    TestStand也有自己的語言嘛?在回答這個(gè)問題之前大家可以想一下在使用TestStand時(shí)有一個(gè)和語言密切相關(guān)的屬性。沒錯(cuò)那就是表達(dá)式(Expressions),在這篇文章中,小編將以Q&A的方式來帶著大家來理解并熟悉TestStand表達(dá)式中較為常用的一些語法規(guī)則以
    的頭像 發(fā)表于 08-15 18:10 ?988次閱讀
    TestStand表達(dá)式中<b class='flag-5'>常用</b>的<b class='flag-5'>語法</b>規(guī)則和運(yùn)算符使用

    FPGA學(xué)習(xí)筆記---基本語法

    Verilog語法是指硬件能夠?qū)崿F(xiàn)的語法。它的子集很小。常用的RTL語法結(jié)構(gòu)如下: 1、模塊聲明:module ... end module
    發(fā)表于 06-23 14:58

    Verilog到VHDL轉(zhuǎn)換的經(jīng)驗(yàn)與技巧總結(jié)

    Verilog與VHDL語法是互通且相互對(duì)應(yīng)的,如何查看二者對(duì)同一硬件結(jié)構(gòu)的描述,可以借助EDA工具,如Vivado,打開Vivado后它里面的語言模板后,也可以對(duì)比查看Verilog和VHDL之間的差異。
    的頭像 發(fā)表于 04-28 17:47 ?2112次閱讀
    <b class='flag-5'>Verilog</b>到VHDL轉(zhuǎn)換的經(jīng)驗(yàn)與技巧總結(jié)

    有什么好用的verilog HDL編輯工具可用?

    有什么好用的verilog HDL編輯工具可用?最好能集成實(shí)時(shí)的verilog HDL語法檢測、自定義模塊識(shí)別觸發(fā)等功能,最好能夠免費(fèi);
    發(fā)表于 04-28 11:00

    fpga是用c語言還是verilog

    FPGA(現(xiàn)場可編程邏輯門陣列)開發(fā)主要使用的編程語言是硬件描述語言(HDL),其中Verilog是最常用的編程語言之一。而C語言通常用于傳統(tǒng)的軟件編程,與FPGA的硬件編程有所區(qū)別。
    的頭像 發(fā)表于 03-27 14:38 ?1701次閱讀

    verilog雙向端口的使用

    輸出信號(hào)。本文將詳細(xì)介紹Verilog雙向端口的使用,并提供示例說明其在實(shí)際應(yīng)用中的作用。 第一部分:雙向端口的定義和語法Verilog中,可以使用wire聲明一個(gè)雙向端口。例如:wire bidirectional_por
    的頭像 發(fā)表于 02-23 10:18 ?1241次閱讀

    verilog中repeat必須用begin和end嗎

    Verilog中,repeat語句不需要使用begin和end塊。repeat語句是一種循環(huán)控制語句,允許重復(fù)執(zhí)行一個(gè)代碼塊指定的次數(shù)。它的一般語法如下: repeat (n) statement
    的頭像 發(fā)表于 02-23 10:14 ?1056次閱讀

    verilog如何調(diào)用其他module

    第一部分:簡介 1.1 什么是Verilog模塊? 在Verilog中,模塊是其設(shè)計(jì)層次結(jié)構(gòu)的基本單元。模塊是一個(gè)用于實(shí)現(xiàn)特定功能的單獨(dú)的硬件單元。它可以是一個(gè)組合邏輯電路,也可以是一個(gè)時(shí)序邏輯電路
    的頭像 發(fā)表于 02-22 15:56 ?5164次閱讀

    verilog task和function區(qū)別

    verilog中的task和function都是用于實(shí)現(xiàn)模塊中的可重復(fù)的功能,并且可以接收參數(shù)和返回結(jié)果。但是它們在編寫和使用上有一些區(qū)別。下面將詳細(xì)介紹task和function的區(qū)別。 語法
    的頭像 發(fā)表于 02-22 15:53 ?948次閱讀

    verilog function函數(shù)的用法

    Verilog 是一種硬件描述語言 (HDL),主要用于描述數(shù)字電子電路的行為和結(jié)構(gòu)。在 Verilog 中,函數(shù) (Function) 是一種用于執(zhí)行特定任務(wù)并返回一個(gè)值的可重用代碼塊。函數(shù)在
    的頭像 發(fā)表于 02-22 15:49 ?4811次閱讀

    oracle的update語法

    Oracle是一種強(qiáng)大的關(guān)系型數(shù)據(jù)庫管理系統(tǒng),具有廣泛的應(yīng)用,UPDATE語句是用于修改數(shù)據(jù)庫中現(xiàn)有記錄的重要操作之一。在本文中,我們將詳細(xì)介紹Oracle的UPDATE語法及其用法。 首先,我們
    的頭像 發(fā)表于 12-05 16:22 ?2283次閱讀

    select語句的基本語法

    SELECT語句是SQL(Structured Query Language,結(jié)構(gòu)化查詢語言)中的一種查詢語句,用于從數(shù)據(jù)庫中檢索數(shù)據(jù)。它是數(shù)據(jù)庫操作中最常用和基本的語句之一。在本文中,我將為您詳盡
    的頭像 發(fā)表于 11-17 16:23 ?1826次閱讀