FPGA超入門 その4-並列処理/逐次処理-

2021/12/01に公開

似たような記述の下記のようなソースコード(抜粋)があります。
VHDLの例

VHDL1
  signal A,B,C : std_logic_vector(3 downto 0) := "0000";
  process(CLK) begin
    if(CLK'event and CLK = '1') then
      A <= A + 1;
      B <= A + 1;
      C <= A + B;
    end if;
  end process;
VHDL2
  process(CLK)
    variable A,B,C : std_logic_vector(3 downto 0) := "0000";
  begin
    if(CLK'event and CLK = '1') then
      A := A + 1;
      B := A + 1;
      C := A + B;
    end if;
  end process;

verilogの例

verilog1
  reg [3:0] A = 4'd0;
  reg [3:0] B = 4'd0;
  reg [3:0] C = 4'd0;
  always@(posedge CLK) begin
    A <= A + 4'd1;
    B <= A + 4'd1;
    C <= A + B;
  end
verilog2
  reg [3:0] A = 4'd0;
  reg [3:0] B = 4'd0;
  reg [3:0] C = 4'd0;
  always@(posedge CLK) begin
    A = A + 4'd1;
    B = A + 4'd1;
    C = A + B;
  end

一見同じように見えますが、VHDLでは<=と:=、verilogでは<=と=というように代入の仕方が異なっています。~1の方はノンブロッキング代入、~2の方はブロッキング代入と呼ばれ、それぞれ回路構成が異なってきます。

動作

実際に上記を動作させると、~1の方は

初期値 1clk目 2clk目 3clk目
A 0 1 2 3
B 0 1 2 3
C 0 0 2 4
~2の方は
初期値 1clk目 2clk目 3clk目
---- ---- ---- ---- ----
A 0 1 2 3
B 0 2 3 4
C 0 3 5 7
と進み、全く別の動作となっています。
ノンブロッキング代入は並列的に同時に処理がされます。A,B,Cそれぞれが1clk前のA,Bの値から計算されています。クロックのタイミングでその時の値から同時に計算されていると考えればよいでしょうか。
ブロッキング代入は逐次的に処理がされます。Aの計算後、そのAの値を使ってBが計算され、そのBの値を使ってさらにCが順番に計算されています。一般的なソフトの記述としてはこちらの方がしっくりくるでしょうか。
VHDL3
  process(CLK)
    variable A,B,C : std_logic_vector(3 downto 0) := "0000";
  begin
    if(CLK'event and CLK = '1') then
      C := A + B;
      B := A + 1;
      A := A + 1;
    end if;
  end process;

のように順番を変えれば、~1と同じ動作にはなると思いますが、混乱の原因となりますので、process文、always文内ではノンブロッキング代入とするのが一般的です。

Discussion