FPGA校招筆試題分析

僅僅給出下面一個電路圖,讓你畫出Q1,Q2以及Q3的波形,並描述電路功能

第一個觸發器的輸入是第二個以及第三個觸發器的輸出的反饋,是Q1與Q2的或非;實際上就是同步三分頻電路

只要觸發器復位有初值即可,一般觸發器復位初值爲0,這裏也默認爲0,那麼輸入值在復位時應該爲1.

那麼當正常運行(復位無效)時,q0的第一個值爲復位值延遲一拍並持續一個時鐘,之後q1、q2就簡單了。

module test(
	input rst_n,
	input clk,
	output out2
 
    );
	
	wire in1;
	
	reg q0, q1, q2;
	
	always@(posedge clk or negedge rst_n) begin
		if(!rst_n) begin
			q0 <= 1'b0;
			q1 <= 1'b0;
			q2 <= 1'b0;
		end
		else begin
			q0 <= in1;
			q1 <= q0;
			q2 <= q1;
		end
	
	end
	
	assign in1 = !q0 & !q1;
	assign out2 = q2;
	
	
	
endmodule

上面的三分頻電路,佔空比爲1/3 ,爲了實現50%的佔空比,需要在第二個觸發器後面加上一個下降沿觸發的觸發器,將第二個觸發器生成的三分頻與延遲一拍輸出的三分頻相或即可,電路圖如下:

module div3(clk,rst_n,dout);
 input clik,rst_n;
 output dout;
 
 reg q0,q1,q2;
 wire d1;
 //上升沿觸發的三分頻,佔空比爲1/3
 always @(posedge clk or negedge rst_n)
  begin
    if(~rst_n)
	  begin
	    q0 <= 1'd0;
		q1 <= 1'd0;
	  end
	else
	  begin
	    //移位
		q0 <= d1;
		q1 <= q1;
	  end
  end
 assign d1=~q0 & ~q1;
 
 //將上升沿觸發的三分頻延時半個週期
 always @(negedge clk or negedge rst_n)
   begin
      if(~rst_n)
	     q2 <= 1'd0;
	  else
	     q2 <= q1;
   end
 
 assign dout=q2 | q1;//相或得出1/2佔空比
endmodule

 對於奇數倍分頻,以N=3爲例:首先使用上升沿觸發生成一個1/3佔空比的時鐘,再用下降沿觸發生成一個1/3佔空比的時鐘(或者將前面的時鐘採用下降沿觸發的FF打一拍),兩者相或就可以得到一個三分頻且佔空比爲1/2的時鐘; 

由D觸發器構成其他的觸發器

//實現JK觸發器
module jk_ff(clk,rst_n,j,k,q);
 input clk,rst_n;
 input j,k;
 output reg q;
 
 always @(posedge clk or negedge rst_n)
  begin
    if(~rst_n)
		q <= 1'd0;
    else
	    q <= d;
  end
  
  assign d=(j && ~q) | (~k && q);
endmodule

//實現T觸發器
module t_ff(clk,rst_n,t,q);
 input clk,rst_n;
 input t;
 output reg q;
 
 always @(posedge clk or negedge rst_n)
  begin
    if(~rst_n)
		q <= 1'd0;
    else
	    q <= d;
  end
  
  assign d=t^q;
endmodule

//實現T‘觸發器
module t1_ff(clk,rst_n,q);
 input clk,rst_n;
 output reg q;
 
 always @(posedge clk or negedge rst_n)
  begin
    if(~rst_n)
		q <= 1'd0;
    else
	    q <= d;
  end
  
  assign d=~q;
endmodule

 

常見的分頻電路原理圖:

實質上就是一個4位的計數器,由Q0輸出爲二分頻,由Q1輸出爲4分頻,由Q2輸出爲8分頻,由Q3輸出爲16分頻;

一般的FPGA中觸發器主要是D觸發器,D觸發器與T觸發器之間的轉換關係如下:

所以用D觸發器來構成上面的計數器,電路圖如下:

 

//計數器其實就是分頻器
module div_2(clk,rst_n,q0,q1,q2,q3);
 input clk,rst_n;
 output reg q0,q1,q2,q3;
 
 wire d0,d1,d2,d3;
 always @(posedge clk or negedge rst_n)
   begin
      if(~rst_n)
	     {q3,q2,q1,q0} <= 4'd0;
	  else
	     {q3,q2,q1,q0} <= {d3,d2,d1,d0};
   end
   
 assign d0=1'd1^q0;
 assign d1=q1^q0;
 assign d2=(q1&q0)^q2;
 assign d3=(q2&q1&q0)^q3;
 //q0輸出爲2分頻,q1輸出爲4分頻,q2輸出爲8分頻,q3輸出爲16分頻,且佔空比都是50%
 //{q3,q2,q1,q0}爲模16計數器
endmodule

任意分頻,實現任意佔空比  

module div(clk,rst_n,clk_o);
  input clk,rst_n;
  output reg clk_o;
 
  parameter M,N;
  reg [3:0] count;
  always @(posedge clk or negedge rst_n)
    begin
	     if(~rst_n)
		   begin
		      clk_o <= 1;
			  count <= 0;
		   end
		 else if(count == M)//佔空比爲M+1/N
		      begin
			    count <= count + 1'd1;
				clk_o <= ~clk_o;
			  end
		 else if(count == N-1)
		      begin
			    count <= 4'd0;
				clk_o <= ~clk_o;
			  end
	     else
		    count <= count + 1'd1; 
	end
endmodule

 

下面是異步4位計數器,不推薦使用:

這種設計每個觸發器都有一定的延時,會出問題的!!慎用

什麼是Clock Jitter和Clock Skew,這兩者有什麼區別?

時鐘抖動(Clock Jitter):指芯片的某一個給定點上時鐘週期發生暫時性變化,使得時鐘週期在不同的週期上可能加長或縮短。

時鐘偏移(Clock Skew):是由於佈線長度及負載不同引起的,導致同一個時鐘信號到達相鄰兩個時序單元的時間不一致。

區別:Jitter是在時鐘發生器內部產生的,和晶振或者PLL內部電路有關,佈線對其沒有影響Skew是由不同佈線長度導致的不同路徑的時鐘上升沿到來的延時不同

什麼是冒險和競爭,如何消除?

下面這個電路,使用了兩個邏輯門,一個非門和一個與門,本來在理想情況下F的輸出應該是一直穩定的0輸出,但是實際上每個門電路從輸入到輸出是一定會有時間延遲的,這個時間通常叫做電路的開關延遲。而且製作工藝、門的種類甚至製造時微小的工藝偏差,都會引起這個開關延遲時間的變化

實際上如果算上邏輯門的延遲的話,那麼F最後就會產生毛刺。信號由於經由不同路徑傳輸達到某一匯合點的時間有先有後的現象,就稱之爲競爭,由於競爭現象所引起的電路輸出發生瞬間錯誤的現象,就稱之爲冒險,FPGA設計中最簡單的避免方法是儘量使用時序邏輯同步輸入輸出;

  1. 加濾波電容,消除毛刺的影響

  2. 加選通信號,避開毛刺

  3. 增加冗餘項,消除邏輯冒險

用verilog實現兩路數據的乘法運算,要求只使用1個乘法器;

input clk ;
input rst_n ; 
input sel_x ; 
input [ 7:0] da_x ;
input [ 7:0] da_y ; 
input [ 7:0] db_x ;
input [ 7:0] db_y ; 
output reg [15:0] dout_x_y ; 
module test(
    input              clk,
    input              rst_n,
    input              sel_x,
    input       [ 7:0] da_x, 
    input       [ 7:0] da_y, 
    input       [ 7:0] db_x, 
    input       [ 7:0] db_y, 
    output  reg [15:0] dout_x_y
    );
    wire    [ 7:0] da;
    wire    [ 7:0] db;
    wire    [15:0] dout;
assign da = sel_x ? da_x : da_y;
assign db = sel_x ? db_y : db_x;
assign dout = da * db ;

always @(posedge clk or negedge rst_n) begin
  if (!rst_n) begin
    dout_x_y <= 16'd0;
  end else begin
    dout_x_y <= dout;
  end
end
endmodule

用verilog實現串並轉換 

 1) lsb優先 2) msb優先

input clk;

rst_n, data_i;

output [7:0] data_o;
module test(
    input clk, rst_n, data_i,
    output [7:0] data_o
    );
    reg [7:0] data_r;
    reg [2:0] cnt;
//lsb
always @(posedge clk or negedge rst_n) begin
  if (!rst_n) begin
    data_r <= 8'd0;
    cnt <= 3'b0;
  end else begin
    data_r <= {data_r[6:0],data_i};
    cnt <= cnt + 1'b1;
  end
end
//msb
/* always @(posedge clk or negedge rst_n) begin
  if (!rst_n) begin
    data_r <= 8'd0;
    cnt <= 3'b0;
  end else begin
    data_r <= {data_i,data_r[7:1]};
    cnt <= cnt + 1'b1;
  end
end */

assign data_o = (cnt == 3'd7) ? data_r : data_o;
endmodule

 用verilog實現乘法y = a * b ,a和b都是8bit,考慮三種情況:

1) 都是無符號數

2) 都是有符號數

3) a是有符號數,b是無符號數

1)module test19(
    input       [ 7:0]  i_a     ,
    input       [ 7:0]  i_b     ,
    output      [15:0]  o_y
    );
assign o_y = i_a * i_b;
endmodule
2)module test19(
    input    signed   [ 7:0]  i_a     ,
    input    signed   [ 7:0]  i_b     ,
    output   signed   [14:0]  o_y
    );

    assign o_y = i_a * i_b;//7+7+1
endmodule
3)module test19(
    input    signed   [ 7:0]  i_a     ,//加上signed後,將數看做二進制補碼
    input             [ 7:0]  i_b     ,//無符號數,也就是正數
    output   signed   [15:0]  o_y
    );
    wire    signed  [8:0]   b_r;
    assign b_r = {1'b0,i_b};//加0不會出錯,因爲正數的補碼與原碼相同

    assign o_y = i_a * b_r;//7+8+1
endmodule

 用Verilog實現 z = abs(x - y)

1) x和y是8bit無符號數

2) x和y是8bit有符號數(2補碼)

 

1)module abs(
    input   [7:0]   i_x,
    input   [7:0]   i_y,
    output [7:0]   o_z
    );

assign  o_z= (i_x > i_y) ? (i_x - i_y) : (i_y - i_x);

endmodule
2)考慮溢出問題,結果應該爲9bit:
module abs(
    input  signed [7:0]   i_x,
    input  signed [7:0]   i_y,
    output        [8:0]   o_z
    );

wire   signed [8:0]   z_r;
assign  z_r = (i_x > i_y) ? (i_x - i_y) : (i_y - i_x);

assign  o_z = z_r;

endmodule

用Verilog實現 y(n) = 0.75x(n) + 0.25y(n-1),x和y是8bit無符號數

module Cal_Formula(
        input           clk,
        input           rst_n,
        input   [7:0]   din,
        output reg [7:0]   dout
    );
    
    // y(n) = 0.75x(n) + 0.25y(n-1) 
    //0.75*4 = 3,0.25*4 = 1
    reg     [7:0]   dout_r1=0;
    reg     [9:0]   dout_r2;
        
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)
            dout_r1 <= 0;
        else 
        dout_r1 <= dout;//將輸出作爲下一個輸入
    end
        
    always @(posedge clk or negedge rst_n)begin
        if(rst_n == 1'b0)begin
            dout_r2 <= 0;
        end
        else begin
            dout_r2 <= 3 * din + dout_r1;
        end
    end
    
    assign 	dout = dout_r2 >> 2; //除4
    endmodule

畫出SRAM bit cell結構圖

SRAM存儲的原理就是基於雙穩態電路,將兩個反相器的輸入與輸出分別交叉連接,然後再利用兩個MOS管作爲字線進行選通,總共需要6個MOS管

D鎖存器和D觸發器的原理圖

狀態機練習,序列檢測

//檢測1101
module detect1101(clk,rst_n,din,mealy_out,moore_out);
 input clk,rst_n;
 input din;
 output reg mealy_out,moore_out;
 
 parameter s0=3'b000,s1=3'b001,s2=3'b010,s3=3'b011,s4=3'b100;
 
 //時序邏輯描述狀態轉移
 reg [2:0] cs,ns;
 
 always @(posedge clk or negedge rst_n)
   begin
      if(~rst_n)
	     begin
		    cs <= s0;
		 end
	  else
	     begin
		    cs <= ns;
		 end
   end
   
 //組合邏輯描述下一個狀態
 always @(*)
  begin
    if(~rst_n)
	   begin
		  ns = s0;
	   end
	else
	   begin
	      ns = s0;//默認狀態
		  case(cs)
		    s0: ns = (din == 1'b1)? s1:s0;
			s1: ns = (din == 1'b1)? s2:s0;
			s2: ns = (din == 1'b1)? s2:s3;
			s3: ns = (din == 1'b1)? s4:s0;
			s4: ns = (din == 1'b1)? s1:s0;
		  endcase
	   end
  end
 //打一拍作爲輸出
 always @(posedge clk or negedge rst_n)
  begin
    if(~rst_n)
	   begin
	      moore_out <= 1'd0;
		  mealy_out <= 1'd0;
	   end
	else
	   begin
	      moore_out <= (cs == s4)? 1'd1:1'd0;//moore機的輸出只與當前狀態有關
		  mealy_out <= (cs == s3 && din == 1'd1)?1'd1:1'd0;//mealy機的輸出與當前狀態和輸入有關
		  //moore機總是比mealy機慢一拍輸出
	   end
  end
endmodule

在clk a時鐘域的一個單週期脈衝信號,如何正確的傳遞到clk b時鐘域? 要考慮clk a和b的各種不同頻率/相位的場景

 

//慢時鐘域到快時鐘域,兩級寄存器同步,也就是打兩拍
    reg signal_in;
    reg     [1:0]   signal_r;
    always @(posedge clk_a or negedge rst_n)begin
        if(rst_n == 1'b0)begin
            signal_in <= 0;
        end
        else begin
            signal_in <= datain;
        end
    end
    //-------------------------------------------------------
    always @(posedge clk_b or negedge rst_n)begin
        if(rst_n == 1'b0)begin
            signal_r <= 2'b00;
        end
        else begin
            signal_r <= {signal_r[0], signal_in};
        end
    end
    
    assign  signal_out = signal_r[1];
//快時鐘域到慢時鐘域
    //Synchronous
    module Sync_Pulse(
        input           clka,
        input           clkb,
        input           rst_n,
        input           pulse_ina,
        output          pulse_outb,
        output          signal_outb
    );
    
    //-------------------------------------------------------
    reg             signal_a;
    reg             signal_b;
    reg     [1:0]   signal_b_r;
    reg     [1:0]   signal_a_r;
    
    //-------------------------------------------------------
    //在clka下,生成展寬信號signal_a
    always @(posedge clka or negedge rst_n)begin
        if(rst_n == 1'b0)begin
            signal_a <= 1'b0;
        end
        else if(pulse_ina == 1'b1)begin
            signal_a <= 1'b1;
        end
        else if(signal_a_r[1] == 1'b1)
            signal_a <= 1'b0;
        else 
            signal_a <= signal_a;//將信號pulse_ina展寬
    end
    
    //-------------------------------------------------------
    //在clkb下同步signal_a
    always @(posedge clkb or negedge rst_n)begin
        if(rst_n == 1'b0)begin
            signal_b <= 1'b0;
        end
        else begin
            signal_b <= signal_a;//0 0 0 ... 1 1 1 
        end
    end
    
    //-------------------------------------------------------
    //在clkb下生成脈衝信號和輸出信號
    always @(posedge clkb or negedge rst_n)begin
        if(rst_n == 1'b0)begin
            signal_b_r <= 2'b00;
        end
        else begin
            signal_b_r <= {signal_b_r[0], signal_b};
        end
    end
    
    assign    pulse_outb = ~signal_b_r[1] & signal_b_r[0];
    assign    signal_outb = signal_b_r[1];//0 0 0 1......
    
    //-------------------------------------------------------
    //在clka下采集signal_b[1],生成signal_a_r[1]用於反饋拉低signal_a
    always @(posedge clka or negedge rst_n)begin
        if(rst_n == 1'b0)begin
            signal_a_r <= 2'b00;
        end
        else begin
            signal_a_r <= {signal_a_r[0], signal_b_r[1]};//反饋,防止脈衝繼續展寬
        end
    end
    
    endmodule

要將一個信號從快時鐘域傳遞到慢時鐘域,首先要在快時鐘域展寬脈衝,然後將展寬的脈衝傳遞到慢時鐘域,如果在慢時鐘域檢測到脈衝後就可以給快時鐘域一個反饋以阻止脈衝的繼續展寬;

 用verilog實現1bit信號邊沿檢測功能,輸出一個週期寬度的脈衝信號

要實現邊緣檢測,採樣時鐘的頻率必須在信號頻率的兩倍以上,否則就很可能出現漏採樣,代碼如下:

module edge_detect(clk,rst,signal,pos_edge,neg_edge,both_edge);
 input clk;
 input rst;
 input signal;
 output pos_edge;
 output neg_edge;
 output both_edge;
 
 reg sig_r0,sig_r1;//狀態寄存器
 always @(posedge clk)
  begin
      if(rst)
	    begin
		   sig_r0 <= 1'b0;
		   sig_r1 <= 1'b0;
		end
	  else
	    begin
		   sig_r0 <= signal;
		   sig_r1 <= sig_r0;
		end
  end
 
 assign pos_edge = ~sig_r1 & sig_r0;//上升沿
 assign neg_edge = sig_r1 & ~sig_r0;//下降沿
 assign both_edge = sig_r0 ^ sig_r1;//雙邊沿檢測
endmodule

用Verilog實現取整函數(ceil、floor、round)。以5bit爲例,小數部分1位,取整後保留4bit 

 wire [4:0] data;

wire [3:0] data_ceil;

wire [3:0] data_floor;

wire [3:0] data_round;

基礎知識:

floor(x),表示不超過x的整數中最大的一個,朝負無窮方向取整;

ceil(x),表示不小於x的整數中最小的一個,朝正無窮方向取整;

round(x)四捨五入到最近的整數

思路:data爲有符號數(2補碼),只有1bit的小數位,精度有限,所以假設data都是小數,例如整數部分是7,則data只能表示大於7.5或小於7.5的小數,不表示整數7;

floor——非負取整即可,負數取整減一;

ceil——正數,取整+1,非正數取整即可;

round——小數位爲1,取整+1;小數位爲0,取整即可 ;

module quzheng(
    input   [4:0] data      ,
    output  [3:0] data_ceil ,
    output  [3:0] data_floor,
    output  [3:0] data_round
    );
    assign data_ceil = (data[4] == 1'b0) ? {1'b0,data[3:1]+1'b1} : data[4:1];//先判斷符號位,向正無窮取整
    assign data_floor = (data[4] == 1'b0) ? data[4:1] : data[4:1]-1'b1;//先判斷符號位,向負無窮取整
    assign data_round = (data[0] == 1'b0) ? data[4:1] : data[4:1]+1'b1;
endmodule

異步fifo的設計

異步fifo主要分爲:雙端口RAM、空滿標誌位判斷邏輯、跨時鐘域指針傳輸模塊

(1)雙端口RAM用於接收空滿判斷邏輯模塊傳送過來的讀寫地址進行讀寫操作;

(2)空滿標誌位判斷邏輯用於判斷full、empty,同時輸出讀寫地址指針以及對應的格雷碼;

(3)跨時鐘域傳輸主要是對地址指針進行打兩拍操作,將讀地址指針傳輸到寫時鐘域用於判斷full,將寫地址指針傳輸到讀時鐘域用於判斷empty;

module sync_r2w //將讀地址傳輸到寫時鐘域
#(parameter ADDRSIZE = 4)
(
    output reg [ADDRSIZE:0] wq2_rptr,
    input      [ADDRSIZE:0] rptr,//輸入的讀地址爲格雷碼
    input                       wclk, wrst_n
);
reg [ADDRSIZE:0] wq1_rptr;
always @(posedge wclk or negedge wrst_n)
    if (!wrst_n) 
        {wq2_rptr,wq1_rptr} <= 0;
    else 
        {wq2_rptr,wq1_rptr} <= {wq1_rptr,rptr};
endmodule
module rptr_empty //在讀時鐘域判斷是否empty
#(parameter ADDRSIZE = 4)
(
    output reg rempty,
    output     [ADDRSIZE-1:0] raddr,
    output reg [ADDRSIZE :0]  rptr,
    input       [ADDRSIZE :0] rq2_wptr,//輸入的寫地址爲格雷碼
    input       rinc, rclk, rrst_n);//rinc爲讀使能
 
 
reg  [ADDRSIZE:0] rbin;
wire [ADDRSIZE:0] rgraynext, rbinnext;
wire  rempty_val;
//-------------------
// GRAYSTYLE2 pointer: gray碼讀地址指針
//-------------------
always @(posedge rclk or negedge rrst_n)
    if (!rrst_n) 
        begin 
            rbin <= 0;
            rptr <= 0;
        end
    else
        begin
            rbin <= rbinnext ; 
            rptr <= rgraynext;//下一個讀地址的格雷碼
        end
// gray碼計數邏輯
assign rbinnext = !rempty ? (rbin + rinc) : rbin;//若不爲空,則讀地址加1作爲下一個讀地址
assign rgraynext = (rbinnext>>1) ^ rbinnext;      //二進制到gray碼的轉換,將右移一位的結果與原來的數異或
        assign raddr = rbin[ADDRSIZE-1:0];//下一個讀地址作爲輸出
//---------------------------------------------------------------
// FIFO empty when the next rptr == synchronized wptr or on reset
//---------------------------------------------------------------
/*
*   讀指針是一個n位的gray碼計數器,比FIFO尋址所需的位寬大一位
*   當讀指針和同步過來的寫指針完全相等時(包括MSB),說明二者折回次數一致,FIFO爲空
*     
*/
assign rempty_val = (rgraynext == rq2_wptr);//比較讀寫指針的格雷碼
        always @(posedge rclk or negedge rrst_n)
if (!rrst_n) 
    rempty <= 1'b1;//若復位,則空信號置1
else 
    rempty <= rempty_val;
endmodule

這裏要注意,對於讀寫指針的位寬通過會比fifo深度所需位寬多出一位,用於判斷是否滿;如果兩個地址指針完全相同,包括最高位也相同,那麼empty;如果兩個指針除了最高位都相同,則full; 

 

同步fifo的設計

module fifo(clk,rst,w_en,r_en,data_in,data_out,empty,full);
 input clk,rst;
 input w_en,r_en;
 input [7:0] datain;
 output reg [7:0] data_out;
 output empty,full;
 
 reg [7:0] ram [15:0];//雙端口RAM
 reg [3:0] w_pt,r_pt;
 reg [3:0] count;
 
 always @(posedge clk or negedge rst_n)
  begin
     if(~rst_n)
	    begin
		    w_pt <= 'd0;
			r_pt <= 'd0;
			count <= 'd0;
			count <= 'd0;
		end
	 else
	    begin
		   case({w_en,r_en})
		      2'b00: count <= count;
			  2'b01:begin//讀
			           data_out <= ram[r_pt];
					   count <= count - 1'd1;
					   r_pt=(r_pt== 4'd15)?0:r_pt+1'd1;
			        end
			  2'b10:begin//寫
			           ram[r_pt] <= data_in;
					   count <= count + 1'd1;
					   w_pt=(w_pt== 4'd15)?0:w_pt+1'd1;
			        end
			  2'b11:begin//寫讀
			           ram[r_pt] <= data_in;
					   //count <= count + 1'd1;
					   w_pt=(w_pt== 4'd15)?0:w_pt+1'd1;
					   data_out <= ram[r_pt];
					   //count <= count - 1'd1;
					   r_pt=(r_pt== 4'd15)?0:r_pt+1'd1;
			        end
		   endcase
		end
  end
  assign full=(count == 4'd15)? 1:0;
  assign empty=(count == 4'd0)? 1:0;
endmodule

用16bit加法器IP覈實現8bit乘法運算

乘法器最通用的實現方式就是使用移位相加來實現,如下:

module mul8bits(
	input clk,
	input rst_n,
 
	input [7:0] a,
	input [7:0] b,
 
	output reg [15:0] result
 
	);
 
	reg [15:0] areg;
	reg [15:0] temp;
	reg [3:0] cnt;
 
	always@(posedge clk or negedge rst_n)begin
		if(~rst_n) begin
			areg <= {8{1'b0}, a};
			temp <= 16'd0;//用來保存移位相加的結果
			cnt <= 4'd0;
			result <= 16'd0;
		end
		else if(cnt <= 7) 
		begin
			if(b[cnt]) begin//判斷b的8個bit是否爲1,如果爲1則加上相應的移位結果
				temp <= temp + areg;
				else //如果爲0則加0
				temp <= temp;
			end
			cnt <= cnt + 1;
			areg <= areg <<1;
		end
		else begin
			cnt <= cnt;
			areg <= {8{1'b0},a};
			result <= temp;
		end
	end
endmodule

將上面移位相加的操作採用16bit的加法器來實現,如下:

module mul8bits(
	input clk,
	input rst_n,
 
	input [7:0] a,
	input [7:0] b,
 
	output reg [15:0] result
 
	);
 
	reg [15:0] areg;
	reg [15:0] temp;
	reg [3:0] cnt;
 
	always@(posedge clk or negedge rst_n)begin
		if(~rst_n) begin
			areg <= {8{1'b0}, a};
			temp <= 16'd0;
			cnt <= 4'd0;
			result <= 16'd0;
		end
		else if(cnt <= 7) begin
			cnt <= cnt + 1;
			areg <= areg <<1;
			if(b[cnt]) temp <= temp_w; //調用IP核
				else temp <= temp;
		end
		else begin
			cnt <= cnt;
			areg <= {8{1'b0},a};
			result <= temp;
		end
	end
	wire [15:0] temp_w;
	add16 inst_add16(
		.in1(temp),
		.in2(areg),
		.sout(temp_w),
		.cout(1'd0)
		);
endmodule

在Verilog中設計模塊以滿足以下要求:
(1)頻率爲100MHz的時鐘
(2)對時鐘敏感的4位寬信號,限制該信號在8到15的範圍內隨機化16次

`timescale 1ns/1ps
module random1();
reg clk;
reg [3:0] out_rand;
 
initial begin
    clk  = 0;
    forever 
        #5 clk = ~clk;
end
 

 
 
integer i;
 
initial begin
    
    for(i = 0;i < 16; i = i + 1) begin
        @(posedge clk) begin
            out_rand = 4'b1000+ {$random}%8;
        end
    end
end
endmodule

反相器的速度與哪些因素有關?什麼是轉換時間(Transition Time)和傳播延遲(Propagation Delay)?

 

反相器的速度與哪些因素有關: 1. 電容(負載電容、自載電容、連線電容)較小,漏端擴散區的面積應儘可能小。輸入電容要考慮: (1)Cgs 隨柵壓而變化(2)密勒效應(3)自舉效應 2. 加大晶體管的尺寸(驅動能力),使晶體管的等效導通電阻(輸出電阻)較小。但這同時加大自載電容和負載電容(下一級晶體管的輸入電容)。 3. 提高電源電壓,提高電源電壓可以降低延時,即用功耗換取性能但超過一定程度後改善有限。電壓過高會引起可靠性問題(氧化層擊穿、熱電子等)。

Transition Time(轉換時間):上升時間:從10%Vdd上升到90%Vdd的時間,下降時間L從90%Vdd下降到10%dd的時間。上升時間和下降時間統稱爲Transition Time,也有定義爲20%到80%。 Propagation Delay(傳播延時):在輸入信號變化到50%Vdd到輸出信號變化到50%Vdd之間的時間

相同面積的cmos與非門和或非門哪個更快?

電子遷移率是空穴的2.5倍(在硅基CMOS工藝中),運算就是用這些大大小小的MOS管驅動後一級的負載電容,翻轉速度和負載大小一級前級驅動能力相關。爲了上升延遲和下降延遲相同,PMOS需要做成NMOS兩倍多大小

載流子的遷移率,對PMOS而言,載流子是空穴;對NMOS而言,載流子是電子;

PMOS採用空穴導電,NMOS採用電子導電,由於PMOS的載流子的遷移率比NMOS的遷移率小,所以,同樣尺寸條件下,PMOS的充電時間要大於NMOS的充電時間長,在互補CMOS電路中,與非門是PMOS管並聯,NMOS管串聯,而或非門正好相反,所以,同樣尺寸條件下,與非門的速度快,所以,在互補CMOS電路中,優先選擇與非門

FPGA與ASIC設計流程的區別

首先我們要清楚FPGA的基本單元是LUT,而ASIC的基本單元是寄存器;

對於FPGA:首先要進行模塊劃分,然後編寫RTL代碼,進行RTL仿真,綜合後將生成的網表文件在FPGA上實現,也就是佈局佈線,該步驟完成後進行STA,沒有問題的話就生成bit流文件,將bit流文件燒錄到FPGA開發板上進行板級調試;

對於ASIC:模塊劃分、RTL設計、驗證、綜合、STA、形式驗證、佈局規劃、DRC設計規則檢查、時鐘樹綜合、生成GDSII文件;

C語言實現冒泡排序

#include <stdio.h>//頭文件
int main(void)
{
	int a[] = {900, 2, 3, -58, 34, 76, 32, 43, 56, -70, 35, -234, 532, 543, 2500};
    int n;  //存放數組a中元素的個數
    int i;  //比較的輪數
    int j;  //每輪比較的次數
    int buf;  //交換數據時用於存放中間數據
	n = sizeof(a) / sizeof(a[0]);  /*a[0]是int型, 佔4字節, 所以總的字節數除以4等於元素的個數*/
	for(i=0;i<n-1;i++)//比較n-1輪
	{
		for(j=0;j<n-i-1;j++)
		{
			if(a[j]>a[j+1])
			{
				buf=a[j];
				a[j]=a[j+1];
				a[j+1]=buf;
			}
		}
	}
	for(i=0;i<n;i++)
	{
		printf("%d\n",a[i]);
	}
	return 0;//注意加上
}

已知一個數據的數值範圍爲1~43,運算並輸出其除3的餘數;

input [5:0] a;//被除數
input [5:0] b;//除數
output reg [5:0] y_shang;
output reg [5:0] y_yu;//商和餘數	
reg [11:0] tempa;
reg [11:0] tempb;

integer i;
always @(*)
 begin
    tempa={6'd0,a};
	tempb={b,6'd0};
	for(i=0;i<6;i++)
	  begin
	    tempa=tempa << 1;//每次左移1位
		tempa=(tempa[11:6]>=tempb[11:6])?(tempa - tempb + 1'b1):tempa;
	  end
	y_yu=tempa[11:6];
	y_shang=tempa[5:0];
 end

//改用時序邏輯,分爲13個週期
always @(posedge clk or negedge rst_n)
  begin
    if(~rst_n)
	   begin
	      y_shang <= 'd0;
		  y_yu <= 'd0;
	   end
	else if(enable)
	  begin
	     y_yu <=tempa[11:6];
	      y_shang <=tempa[5:0];
	  end
	else
	  begin
	      y_shang <= 'd0;
		  y_yu <= 'd0;
	   end
  end 

reg [3:0] count;
always @(posedge clk or negedge rst_n)
  begin
    if(~rst_n)
	   count <= 'd0;
	else if(count == 4'd12)
	   count <= 'd0;
	else 
	   count <= count + 1'd1;
end

always @(posedge clk or negedge rst_n)
 begin
   if(~rst_n)
     begin
	    tempa <= 'd0;
		tempb <= 'd0;
	 end
	else if(count == 'd0)
	    begin
		   tempa <= {6'd0,a};
		   tempb <= {b,6'd0}
		end
	else if(count[0] == 1'b1)
	       tempa <= tempa << 1;
	else
	    tempa <= (tempa[11:6] >= tempb[11:6])? (tempa - tempb + 1'b1):tempa;
 end 
 
 reg enable;
 always @(posedge clk or negedge rst_n)
  begin
    if(~rst_n)
	   enable <= 1'd0;
	//else if(count == 0)
	 //  enable <= 1'd0;
	else if(count == 4'd12)
	   enable <= 1'd1;
	else
	   enable <= 1'd0;
  end

二進制序列檢測的移位寄存器實現

以前看到二進制序列檢測一般都使用狀態機實現,今天突然想到可以使用移位寄存器實現,移位寄存器就好像一個窗口,把從窗口裏截取的數據接到一個比較器上,這樣比較不同序列時只要換一下比較器另一個輸入端的數據就可以實現,需要改變的只是寄存器和比較器的位寬;

module seq_det(iBit,CLK,RSTn,iKEY,Q);
input   iBit;
input[4:0]  iKEY;
input   CLK;
input   RSTn;
output   Q;

reg[4:0]  shift_reg;

always@(posedge CLK or negedge RSTn)
begin
 if(RSTn==0)
  shift_reg <= 0;
 else
  shift_reg <= {shift_reg[3:0],iBit};
end

assign Q =(shift_reg==iKEY)?1:0;
endmodule

定義一個隨機數,並約束其範圍爲4~100,並建倉

rand bit [6:0] a;

constraint value_a{a>4;
                   a<100;}
				   
covergroup ag @(posedge clk)
  coverpoint : a {bins a1[]={[4:100]};}
endgroup

將兩個乘法器減少爲1個,同時數據選擇器多用了1個;

fir濾波器的設計

module fir(clk,rst,din,dout,ordy);
 input clk;
 input rst;
 input [7:0] din;
 output [15:0] dout;
 output ordy;
 //matlab fir生成係數 * 256  該濾波器採樣率爲100Hz,截止頻率爲10Hz
 parameter coeff1=8'd5,coeff2=8'd17,coeff3=8'd43,coeff4=8'd63,coeff5=8'd63,coeff6=8'd43,coeff7=8'd17,coeff8=8'd5;
 //8個寄存器
 reg  signed [7:0]  sample_1;
 reg  signed [7:0]  sample_2;
 reg  signed [7:0]  sample_3;
 reg  signed [7:0]  sample_4;
 reg  signed [7:0]  sample_5;
 reg  signed [7:0]  sample_6;
 reg  signed [7:0]  sample_7;
 reg  signed [7:0]  sample_8;
 
 reg [18:0] dout;
 reg ordy;
 //輸入數據,移位寄存
 always @(posedge clk )
   begin
        if(rst)
		   begin
		        sample_1 <= 8'd0;
				sample_2 <= 8'd0;
				sample_3 <= 8'd0;
				sample_4 <= 8'd0;
				sample_5 <= 8'd0;
				sample_6 <= 8'd0;
				sample_7 <= 8'd0;
				sample_8 <= 8'd0;
		   end
		else
		   begin
		        sample_1 <= din;
				sample_2 <= sample_1;
				sample_3 <= sample_2;
				sample_4 <= sample_3;
				sample_5 <= sample_4;
				sample_6 <= sample_5;
				sample_7 <= sample_6;
				sample_8 <= sample_7;//8個週期完成移位,完成滯後
		   end
   end
 //調用ip,執行乘法
 wire [15:0] p[8:1];
 mult_8 u1 (
  .CLK(clk),  // input wire CLK
  .A(sample_1),      // input wire [7 : 0] A
  .B(coeff1),      // input wire [7 : 0] B
  .P(p[1])      // output wire [15 : 0] P 設置pipline stage 爲3,表示3級延時
);
 mult_8 u2 (
  .CLK(clk),  // input wire CLK
  .A(sample_2),      // input wire [7 : 0] A
  .B(coeff2),      // input wire [7 : 0] B
  .P(p[2])      // output wire [15 : 0] P
);
 mult_8 u3 (
  .CLK(clk),  // input wire CLK
  .A(sample_3),      // input wire [7 : 0] A
  .B(coeff3),      // input wire [7 : 0] B
  .P(p[3])      // output wire [15 : 0] P
);
 mult_8 u4 (
  .CLK(clk),  // input wire CLK
  .A(sample_4),      // input wire [7 : 0] A
  .B(coeff1),      // input wire [7 : 0] B
  .P(p[4])      // output wire [15 : 0] P
);
 mult_8 u5 (
  .CLK(clk),  // input wire CLK
  .A(sample_5),      // input wire [7 : 0] A
  .B(coeff5),      // input wire [7 : 0] B
  .P(p[5])      // output wire [15 : 0] P
);
 mult_8 u6 (
  .CLK(clk),  // input wire CLK
  .A(sample_6),      // input wire [7 : 0] A
  .B(coeff6),      // input wire [7 : 0] B
  .P(p[6])      // output wire [15 : 0] P
);
 mult_8 u7 (
  .CLK(clk),  // input wire CLK
  .A(sample_7),      // input wire [7 : 0] A
  .B(coeff7),      // input wire [7 : 0] B
  .P(p[7])      // output wire [15 : 0] P
);
 mult_8 u8 (
  .CLK(clk),  // input wire CLK
  .A(sample_8),      // input wire [7 : 0] A
  .B(coeff8),      // input wire [7 : 0] B
  .P(p[8])      // output wire [15 : 0] P
);
 //加法第一級
 wire [16:0] s1 [4:1];//加法器的延時爲2
 add_16 a1 (
  .A(p[1]),      // input wire [15 : 0] A
  .B(p[2]),      // input wire [15 : 0] B
  .CLK(clk),  // input wire CLK
  .S(s1[1])      // output wire [16 : 0] S
);
 add_16 a2 (
  .A(p[3]),      // input wire [15 : 0] A
  .B(p[4]),      // input wire [15 : 0] B
  .CLK(clk),  // input wire CLK
  .S(s1[2])      // output wire [16 : 0] S
);
 add_16 a3 (
  .A(p[5]),      // input wire [15 : 0] A
  .B(p[6]),      // input wire [15 : 0] B
  .CLK(clk),  // input wire CLK
  .S(s1[3])      // output wire [16 : 0] S
);
 add_16 a4 (
  .A(p[7]),      // input wire [15 : 0] A
  .B(p[8]),      // input wire [15 : 0] B
  .CLK(clk),  // input wire CLK
  .S(s1[4])      // output wire [16 : 0] S
);
 //加法第二級
 wire [17:0] s2 [2:1];
 add_17 a21 (
  .A(s1[1]),      // input wire [16 : 0] A
  .B(s1[2]),      // input wire [16 : 0] B
  .CLK(clk),  // input wire CLK
  .S(s2[1])      // output wire [17 : 0] S
);
 add_17 a22 (
  .A(s1[3]),      // input wire [16 : 0] A
  .B(s1[4]),      // input wire [16 : 0] B
  .CLK(clk),  // input wire CLK
  .S(s2[2])      // output wire [17 : 0] S
);
 //加法第三級
 wire [18:0] s3;
 add_18 a31 (
  .A(s2[1]),      // input wire [17 : 0] A
  .B(s2[2]),      // input wire [17 : 0] B
  .CLK(clk),  // input wire CLK
  .S(s3)      // output wire [18 : 0] S
);
 //計數
 reg [4:0] counter;
 always @(posedge clk)
  begin
      if(rst)
	    begin
		   counter <= 5'd0;
		   dout <= 19'd0;
		   ordy <= 1'b0;
		end
	  else if(counter == 17)
	    begin
	       dout <= s3;
		   ordy <= 1'b1;
		end
	  else
	       begin
		       dout <= 19'd0;
			   counter <= counter + 1'b1;
		   end
  end
endmodule

#include <stdio.h>

int main()
{
   char *str=(char *)malloc(100);
	   strcpy(str,"hello");
	printf("%s\n",str);
	free(str);//釋放了str所指向地址中的內容,並沒有釋放指針
	printf("%d\n",&str);
	printf("%s\n",str);
	if(str != NULL)
	{
		strcpy(str,"world");
	    printf("%s\n",str);
	}
}

在測試文件裏,always是一個循環,就像我們寫生成時鐘一樣,有種寫法就是:

initial clk = 0;
 
always begin
#2 clk = ~clk;
 
end

那這裏的always同樣也是循環語句的作用,內部是阻塞賦值,@(A)以及@(B)相當於觸發條件,#2其實也屬於這一類,過了2ns執行下面的語句,這裏就是A變化了就觸發,題目中說A在15ns以及25ns各觸發一次,當A在15ns觸發一次後,count = 2,然後等待@(B),可是B一直爲X態,沒有觸發,所以就一直阻塞到這裏了,再多的時間也沒用。所以只能最後count = 2.

設計一個可預置初值的7進制循環計數器

module counter7(clk,rst,load,data,cout);
    input clk,rst,load;
    input [2:0] data;
    output reg [2:0] cout;
    always@(posedge clk)
    begin
        if(!rst)
        cout<=3’d0;
        else if(load)
        cout<=data;      
        else if(cout>=3’d6)
        cout<=3’d0;
        else
        cout<=cout+3’d1;//不同位寬的數相加會自動補齊
    end
endmodule

用verilog實現冒泡排序

module sort(clk,rst,datain,dataout,over);

parameter length=8;           // the bits number of data
parameter weikuan=256;         // the length of the memory

input clk,rst;
input[length-1:0] datain;
output[length-1:0] dataout;
output over;

reg over;
reg [length-1:0] dataout;
reg [length-1:0] memo[weikuan-1:0];

integer i,j,m;

//**************數據交換任務模塊************//
task exchange;
  inout[length-1:0] x,y;  
  reg[length-1:0] temp;

  begin
    if(x<y)
      begin
        temp=x;
        x=y;
        y=temp;
      end
  end
endtask
//***********************************************

always@(negedge clk or posedge rst)
if(!rst)
  begin
    i=0;
    j=0;
    m=0;
    over=0;
  end
else
  if(m==weikuan-1)  //the memory is full
    begin
      m=weikuan-1;
     
      if(i==weikuan) //arrangement is over, set over to be  "1"
        begin
          i=weikuan;
          over=1;
        end
       
      if(i<weikuan)
        for(i=0;i<weikuan;i=i+1)        //then  put the datas in order
          begin
            for(j=0;j<weikuan-1-i;j=j+1) //note the range of 'j'
            exchange(memo[j+1],memo[j]); //if 'memo[j+1]<memo[j]', exchange them.
          end
    end
  else            //input the data first
    begin
      memo[m]=datain;  
      m=m+1;
    end                                    
 
endmodule

數字倍頻電路

奉上代碼:


module clk_mul(
    input   wire            clk
,   input   wire            rst_n
,   output  wire            clk_out
);
//////////////////////////////////////////////////////////////////////////////
// variable declaration
reg     temp_mul    ;
//////////////////////////////////////////////////////////////////////////////
// logic
always @(posedge clk_out or negedge rst_n) begin
    if(~rst_n)                      temp_mul    <=  1'b0            ;
    else                            temp_mul    <=  #2 ~temp_mul    ;
end
assign  clk_out =   ~(clk ^ ~temp_mul)  ;


endmodule   //          CREATED by poiu_elab@1207

這個東西很簡單的,但是要注意,它的核心是第16行的#2,這裏的延時(佔空比)可以通過電路的Tco和經過反相器的時間來搞定(其中可以插入一些buffer來調節時間),testbench這麼來寫(很簡單,但是便於下面解釋延時對佔空比的影響還是附上):

`define CLK_CYCLE   20
module tb();
///////////////////////////////////////////////////////////////////////////////
// variable declaration
reg             clk         ;
reg             rst_n       ;
wire            clk_out     ;
///////////////////////////////////////////////////////////////////////////////
// stimulation generation
initial forever #(`CLK_CYCLE/2) clk = ~clk;
initial begin
    rst_n           =   1'b0            ;
    clk             =   1'b1            ;
#500;
    rst_n           =   1'b1            ;
#5000;
$stop;
end
///////////////////////////////////////////////////////////////////////////////
// module instaniation
clk_mul u_clk_mul(
    .clk        (   clk     )
,   .rst_n      (   rst_n   )
,   .clk_out    (   clk_out )
);
///////////////////////////////////////////////////////////////////////////////

endmodule   

下面給出仿真圖,當#2的時候,是這樣的,其中你要關心的其實是~temp_mul & clk, 當你要是q端經過反相器的信號與接入的clk信號相同的時候你的clk_out就會起來,之後你的q端翻轉了的話,你的clk_out就會落下來,這時候在下一個clk翻轉的時候,你的~temp_mul & clk就會又要把clk_out拉起來,q端又翻轉,以此類推,就可以繼續在每個clk的跳變沿出現你的clk_out的高電平,調整你的Tco和反相器延時就可以調整你的高電平時間,由於週期又是固定的,這樣就可以調整你的佔空比;

而當#5的時候,是這樣的

看出什麼端倪了沒,當你的延時,正好是時鐘週期的1/4的時候,你就可以得到一個佔空比是50%的2倍頻時鐘;

要求:一個握手的模塊。輸入信號分別爲en,ack,all_done;輸出信號是req,done;要求例如以下en高電平有效時能夠輸出req信號。然後等待ack信號,收到ack信號後會發出done信號,模塊的數量不定。當全部模塊done信號軍發送完成後會接收到all_done信號,僅僅有接收到aLL_done信號才幹夠發送下一次req信號

module handshake(input clk, rst_n,en,all_done,ack,

                               output reg req, done);

reg flag,r_all;


always @(posedge clk or negedge rest_n)

if(!rst_n)begin

  flag<=1'b1;//推斷是否爲第一次發送,flag爲標誌位

  r_all<=1'b0; //是否接收到all_done信號

end

else if(all_done) begin

    r_all<=1'b1;//已接收到all_done信號置位

end

else if(done) begin

   r_all<=1'b0;//以完畢發送,all_done信號清零

   flag<=1'b0;//完畢第一次發送後即永久性清零。

end

always @(posedge clk or negedge rest_n)

if(!rst_n)begin

   req<=1'b0;

  done<=1'b0;

end

else begin

   if((en&flag) || (en&all_done) || (en &r_all)) 

         req<=1'b1;

   else  req<=1'b0;

  if(ack)

      done<=1'b1;

  else  done<=1'b0;

end



endmodule

一個1bit的信號,脈衝寬度爲一個源時鐘週期,需要傳送到到頻率低得多的目的時鐘域,比方說源時鐘頻率是目的時鐘頻率的三倍,不能採用握手,不能在信源對信號展寬,怎樣在目的時鐘域識別到這個信號的脈衝?fifo等存儲器手段也不能用,而且沒有隨行的同步時鐘信號

邊沿檢測同步器適用情況爲:異步輸入信號的脈衝寬度小於時鐘週期時的同步電路(高速時鐘域→低速時鐘域)

上圖中包括3條路徑,對於路徑1/2/3的數據路徑分別爲:

寄存器前門訪問和後門訪問的區別

前門訪問通過總線來訪問寄存器需要消耗仿真時間;而後門訪問通過UVM DPI關聯硬件寄存器路徑,直接讀取或修改硬件,不需要消耗仿真時間;

前門訪問一般只能按字(word)進行讀寫,無法直接訪問寄存器域;後門可以對寄存器或寄存器域進行讀寫;

實現1+2+3+4+.......

module Sum_n(input clk,rst_n,
input en,//單脈衝有效信號;
input [10:0]data_in,
output reg [29:0]sum_n,
output reg D_en//高電平表示和有效;
);
reg [10:0]data_buf;//data_1,
reg [1:0]state;
parameter Wait_signal = 2'b00,
          judge_data = 2'b01,
          data_sub = 2'b11,
          clr_en = 2'b10;
always@( posedge clk )
   begin
       if( !rst_n )
           begin
              sum_n <= 0;
              D_en <= 0;
              state <= Wait_signal;
           end
       else
           begin
              case ( state )
                    Wait_signal : begin//默認態
                             if( en )
                                  begin
                                     data_buf <= data_in;
                                     state <= judge_data;
                                  end
                              else
                                   state <= Wait_signal;
                                   end
                    judge_data : begin//判決相加態
                              if( data_buf >= 1'b1 )
                                 begin
                                    sum_n <= sum_n + data_buf;
                                    state <= data_sub;
                                 end
                               else
                                 begin
                                     //sum_n <= 0;
                                     D_en <= 1;
                                     state <= clr_en;
                                 end
                                  end
                    data_sub : begin//減1態
                                  data_buf <= data_buf - 1'b1;
                                  state <= judge_data;
                               end
                    clr_en : begin//清零態
                               D_en <= 0;
                               state <= Wait_signal;
                             end
              endcase
           end
   end
endmodule

異步跨時鐘域處理

對於單bit數據,可以採用打兩拍的方式,但是這種方式只能適用於從慢時鐘到快時鐘;對於從快到慢的跨時鐘域處理,需要用到脈衝同步器,如下所示:

module edge_detect(
                    input sclk_1,//100M
                    input sclk_2,//50M
                    input p_in,
                    output p_out
                    );

    reg p_in_reg=0;
    reg dly1,dly2,dly3;
    wire mux_2;

    //組合邏輯產生毛刺,p_in沒有和sclk_1 同步
    assign mux_2 = (p_in==1'b1)? (~p_in_reg):p_in_reg;

    always @(posedge sclk_1)
        p_in_reg <= mux_2;

    always @(posedge sclk_2)
        {dly3,dly2,dly1} <= {dly2,dly1,p_in_reg};//流水移位賦值

    assign p_out = dly3 ^ dly2;

endmodule

對於多bit數據,一般採用異步fifo來實現,也可以使用握手協議,但是握手效率低,適用於低速的情況;

採用握手機制:發送域現將數據鎖存在總線上,隨後發送一個req信號給接受域;接受域在檢測到req信號後,鎖存總線數據,並送回一個有效的ack信號表示讀取完成應答;檢測到應答信號ack後,撤銷req信號,接受域在檢測到req撤銷後也相應撤銷ack信號,完成一次正常的握手信號;

module handshake(
        input clk,                 //50MHZ時鐘
        input rst_n,               //復位信號
        input req,                //數據發送請求信號
        input [15:0]data_in,        //數據輸入
        output reg ack,            //應答信號
        output [15:0]data_out
);
 
reg req1,req2,req3;                //req輸入同步信號
reg [15:0]data_in_r;                //輸入數據寄存器
wire pos_req1,pos_req2;
//---------通過三級寄存器同步異步輸入信號req--------
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
         req1 <= 1'b0;
         req2 <= 1'b0;
         req3 <= 1'b0;
    end
    else begin
        req1 <= req;
        req2 <= req1;
        req3 <= req2;
    end
end
//--------------檢測req1、req2的上升沿---------------
assign pos_req1 = req1 && ~req2;
assign pos_req2 = req2 && ~req3;
//----------在檢測到pos_req1上升沿時,鎖存數據-------
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        data_in_r <= 16'd0;
    end
    else begin
        if(pos_req1)begin//檢測到上升沿,發送數據
            data_in_r <= data_in;
        end
    end
end
assign data_out = data_in_r;
//----------在檢測到pos_req2上升沿時,發出ack應答信號,表示數據已經鎖存-------
//----------檢測req信號,如果req信號取消,則ack也取消-----------------------
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        ack <= 1'b0;
    end
    else begin
        if(pos_req2)begin
            ack <= 1'b1;
        end
        else if(!req)begin
            ack <= 1'b0;
        end
    end
end
endmodule

parameter與define的區別

parameter聲明和作用於模塊內部,可以在調用模塊時進行參數傳遞
define是全局作用,從編譯器讀到這條指令開始到編譯結束都有效,或者遇到`undef命令使之失效;

如何驗證一個異步FIFO?

驗證分爲驗證場景和驗證平臺。驗證場景如表所示:

注:在驗證復位場景時,完整的復位驗證應當包含以下情形的復位:

初始復位:在驗證激勵施加之前對讀、寫指針的復位,可以在driver中完成。

寫復位:在FIFO寫入過程中對寫時鐘域信號復位;在FIFO讀出過程中對寫時鐘域信號復位。

讀復位:在FIFO讀出過程中對讀時鐘域信號復位;在FIFO寫入過程中對讀時鐘域信號復位。

隨機復位:任意情形下的隨機復位。

   在完成驗證平臺和驗證場景的設計後,還需添加斷言用於檢查部分時序,所構建的斷言應當包括以下點:

(1)檢查復位時所有指針和滿/空信號是否都處於低電平狀態。

(2)當fifo讀計數爲0時,檢查fifo_empty是否被置位。

(3)當fifo寫計數大於FIFO寬度時,檢查fifo_full是否被置位。

(4)檢查如果fifo已滿,並且嘗試寫入時寫入指針不會更改。

(5)檢查如果fifo已空,並且嘗試讀出時讀指針不會更改。

(6)有一個屬性會在嘗試fifo寫滿時發出警告。

(7)有一個屬性會在嘗試fifo讀空時發出警告。

(8)確保在WRITE模式下,寫輸入指針處於已定義狀態(0或1),它不應爲X態.同樣適用於READ模式。

 什麼是線與邏輯?

線與是指兩個輸出信號相連可以實現與的功能;在硬件上需要用OC門來實現,由於OC門可能使灌電流過大而燒壞邏輯門,需要在輸出端口加上一個上拉電阻;

看下面代碼復位爲什麼會失敗?

reg rst_n;
reg f0_rst_n;
reg f1_rst_n;
wire rst_n_out;
always @(posedge clk) 
begin
   f0_rst_n <= rst_n;
   f1_rst_n <= f0_rst_n;
end

rst_n_out = rst_n | f0_rst_n | f1_rst_n;

f0_rst_n,f1_rst_n在被賦值以前都是x態,該代碼的本意是將復位信號展寬,但是由於X態的存在會將或門的復位信號變爲X態從而導致復位失敗;

C語言實現斐波那契數列

int fibo1(int n)
{
    if (n == 1 || n==2)
    {
        return 1;
    }
        return fibo1(n-2)+fibo1(n - 1);
        
}

 

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