2.4 Verilog Language - Procedures

Kiwoong Nam·2025년 4월 12일

HDLBits

목록 보기
4/11

Last update: 25-04-12

Always blocks (combinational)

Alwaysblock1

module top_module(
    input a, 
    input b,
    output wire out_assign,
    output reg out_alwaysblock
);
    assign out_assign = a & b;
    always @(*)
        out_alwaysblock = a & b;

endmodule

always @(*) 구문을 보여주는 예제이다.

  • Combinational: always @(*)
  • Clocked: always @(posedge clk)

Combinational always 블록은 assign 구문과 동일한, 조합논리를 표현하는 방법이다. 둘 중 편한 걸 사용하면 되지만, if-then, case 등의 블록 안에서는 assign을 사용할 수 없다.

Always blocks (clocked)

Alwaysblock2

module top_module(
    input clk,
    input a,
    input b,
    output wire out_assign,
    output reg out_always_comb,
    output reg out_always_ff   );
    assign out_assign = a ^ b;
    always @(*) out_always_comb = a ^ b;
    always @(posedge clk) out_always_ff <= a ^ b;

endmodule

세 가지 assignment 방식에 대해 알려주는 문제이다.

Blocking vs. Non-Blocking Assignment

  • Continuous assignments (assign x = y;). Can only be used when not inside a procedure ("always block").
  • Procedural blocking assignment: (x = y;). Can only be used inside a procedure.
  • Procedural non-blocking assignment: (x <= y;). Can only be used inside a procedure.

procedure는 always, initial, task, function 블록을 말한다. 이런 프로시저 안에서는 블로킹/논블로킹 assignment를 사용해야 하고, 밖에서는 assign 구문을 사용해야 한다.

Combinational의 경우 blocking assignment를 사용하고, Clocked의 경우 non-blocking assignment를 사용한다. 그렇게 하는 편이 비결정적인 동작이나 합성 과정에서 발생하는 오류를 줄이고 해결하기 쉽게 해 준다고 한다.

If statement

Always if

module top_module(
    input a,
    input b,
    input sel_b1,
    input sel_b2,
    output wire out_assign,
    output reg out_always   ); 
    assign out_assign = sel_b1 & sel_b2 ? b : a;
    always @(*) begin
        if (sel_b1 & sel_b2) out_always = b;
        else out_always = a;
    end

endmodule

? : 구문 대신 if ~ else를 사용하는 문제이다. if문을 사용할 때 주의할 점으로, 모든 경우에 대해 out에 assign해주지 않으면 조합논리로 생성되지 않는다!!! 반드시 모든 케이스를 커버해야 한다.

If statement latches

Always if2

module top_module (
    input      cpu_overheated,
    output reg shut_off_computer,
    input      arrived,
    input      gas_tank_empty,
    output reg keep_driving  ); //

    always @(*) begin
        if (cpu_overheated)
           shut_off_computer = 1;
        else shut_off_computer = 0;
    end

    always @(*) begin
        if (~arrived)
           keep_driving = ~gas_tank_empty;
        else keep_driving = 0;
    end

endmodule

위에서 말한 모든 경우를 커버하지 않으면 조합논리가 생성되지 않는다를 체험시켜주는 문제이다. 난 다른 이유로 틀렸는데, 문제를 제대로 안 읽고 마지막에 else keep_driving = gas_tank_empty;로 써서 틀렸다.

Case statement

Always case

module top_module ( 
    input [2:0] sel, 
    input [3:0] data0,
    input [3:0] data1,
    input [3:0] data2,
    input [3:0] data3,
    input [3:0] data4,
    input [3:0] data5,
    output reg [3:0] out   );//

    always@(*) begin  // This is a combinational circuit
        case(sel)
            3'b000: out = data0;
            3'b001: out = data1;
            3'b010: out = data2;
            3'b011: out = data3;
            3'b100: out = data4;
            3'b101: out = data5;
            default: out = 0;
        endcase
    end

endmodule

C언어의 switch 구문과 비슷해서 헷갈렸는데, 얘는 case로 시작하며 case item들을 바로 숫자로 나타낸다. begin을 사용하지 않으며 endcase로 끝을 낸다. break가 없지만 하나의 case item만 실행된다.
각 case item별 실행 구문은 1문장이 기본으로, 여러 문장을 쓰려면 begin ~ and를 사용한다.
똑같은 case item을 여러 개 쓰거나 중첩되는 경우가 있을 경우, 가장 먼저 선언된 구문만 실행된다.
위의 래치 문제와 마찬가지로 모든 경우를 커버하지 않을 경우 래치가 발생할 수 있다. 아마도

Priority encoder

Always case2

module top_module (
    input [3:0] in,
    output reg [1:0] pos  );
    always @(*) begin
//        casex (in)
//            4'bxxx1: pos = 0;
//            4'bxx1x: pos = 1;
//            4'bx1xx: pos = 2;
//            4'b1xxx: pos = 3;
//            default: pos = 0;
//        endcase
        case(in)
            4'b0000: pos = 0;
            4'b0001: pos = 0;
            4'b0010: pos = 1;
            4'b0011: pos = 0;
            4'b0100: pos = 2;
            4'b0101: pos = 0;
            4'b0110: pos = 1;
            4'b0111: pos = 0;
            4'b1000: pos = 3;
            4'b1001: pos = 0;
            4'b1010: pos = 1;
            4'b1011: pos = 0;
            4'b1100: pos = 2;
            4'b1101: pos = 0;
            4'b1110: pos = 1;
            4'b1111: pos = 0;
        endcase
    end

endmodule

x, z의 사용을 수업시간에 이미 들은 탓에 진도를 너무 빨리 나갔다.

Priority encoder with casez

Always casez

module top_module (
    input [7:0] in,
    output reg [2:0] pos );
    always @(*) begin
        casez (in)
            8'bzzzzzzz1: pos = 0;
            8'bzzzzzz1z: pos = 1;
            8'bzzzzz1zz: pos = 2;
            8'bzzzz1zzz: pos = 3;
            8'bzzz1zzzz: pos = 4;
            8'bzz1zzzzz: pos = 5;
            8'bz1zzzzzz: pos = 6;
            8'b1zzzzzzz: pos = 7;
            default: pos = 0;
        endcase
    end

endmodule

casez 구문에서 z는 don't-care로 처리된다. 같은 의미로 ?을 사용할 수도 있다.
casex 구문의 경우 x와 z를 모두 don't-care로 처리하는데, 이 구문 자체가 잘 안 쓰인다고 한다.
한편 일반 case 구문에서도 x, z를 사용할 수 있는데, 출력에 high-z를 주는 경우 등에 사용하는 듯 하다.

Avoiding latches

Always nolatches

module top_module (
    input [15:0] scancode,
    output reg left,
    output reg down,
    output reg right,
    output reg up  ); 
    always @(*) begin
        up = 0; down = 0; right = 0; left = 0;
        case (scancode)
            16'he06b: left = 1;
            16'he072: down = 1;
            16'he074: right = 1;
            16'he075: up = 1;
        endcase
    end

endmodule

단순히 default를 집어넣는 것만으로는 래치의 발생을 막을 수 없다는 걸 설명해주는 문제다. case문 안에서 건드리는 left, down, right, up이 모든 경우에서 각자 assign이 되어야 한다. 다만 각 경우에 대해 네 개를 모두 assign하는 것은 귀찮으므로, case문 이전에 default value를 설정해 래치를 더 간단히 방지할 수 있다. 이렇게 하면 default case item도 필요가 없다!!

0개의 댓글