반도체, 임베디드 Study/디지털 설계

Verilog - 기본 문법 정리

잇(IT) 2024. 3. 30. 14:40
728x90

- 자료형과 연산자

- Verilog 연산자

- 게이트 수준 모델링

- 할당문

- 행위수준 모델링

- 구조적 모델링


자료형과 연산자

Verilog 자료형

논리값

0 : 거짓, 1 : 참, x : unknown, z : high-impedance (구동자가 없는 상태)

net 자료형

wire, tri, wand, wor, triand, trior, supply0, supply1, tri0, tri1, trireg

net는 값을 저장하지 않으며(단, trireg net는 예외임), 연속 할당문, 게이트 프리미티브 등과 같은 구동자의 값에 의해 net의 값이 연속적으로 유지된다.

net에 구동자가 연결되어 있지 않으면, default 값인 high-impedance(z)가 된다.

net 자료형을 선언하는 문법은 net, parameter, variable 선언에 의해 이미 선언된 이름을 다시 선언하는 것은 허용되지 않는다.

variable 자료형

reg, integer, real, time, realtime

variable 자료형을 갖는 변수는 절차형 할당문의 실행에 의해 그 값이 바뀌며, 할당에서부터 다음 할당까지 값을 저장하므로 프로그래밍 언어의 variable과 유사한 개념이라고 볼 수 있다.

reg, time, integer 자료형의 기본 초기 값은 x이며, real, realtime 자료형의 초기 값은 0.0이다.

벡터와 배열

범위지정을 갖지 않는 net나 reg 선언은 1비트로 취급된다. Verilog HDL에는 2비트 이상으로 구성되는 벡터를 선언하기 위한 독립된 자료형이 없으며, net 또는 reg 자료형 선언에서 변수이름 앞에 범위 지정 [msb:lsb]를 추가하여 벡터를 선언할 수 있다.

reg [7:0] mem_A [0:255]; 
// reg mem_A는 8비트 레지스터 256개로 구성되는 메모리에 대한 선언
reg array_B [7:0][0:255] 
// reg array_B는 1비트 레지스터가 8x256 크기로 구성된 2차원 배열의 선언

wire [7:0] Array_2D [0:255][0:255] 
// Array_2D는 8비트 워드의 256x256 크기 2차원 배열로 선언된다.
assign Array_2D[14][1][3:0] = 4'b1100;
// Array_2D는 15번째 행, 2번째 열 워드의 하위 4비트에 4'b1100을 할당한다.

parameter

paramter는 variable 또는 net 범주에 속하지 않는 상수 값이다.


Verilog 연산자

시프트 연산자

왼쪽 논리시프트 연산자, 산술시프트 연산자는 우측의 피연산자 값만큼 좌측의 피연산자를 왼쪽으로 이동시킨다. 왼쪽 시프트 연산자는 시프트 후에 비어 있는 비트에 0을 채워 값을 만든다.

오른쪽 논리시프트 연산자, 산술시프트 연산자는 우측의 피연산자 값만큼 좌측의 피연산자를 오른쪽으로 이동시킨다.

오른쪽 시프트 연산 후에 비어 있는 비트들을 채우는 방법에 있어서 차이를 갖는다. 논리시프트 연산자는 시프트 연산 후에 비어 있는 비트들을 채우는 방법에 있어서 차이를 갖는다.

논리시프트 연산자는 시프트 연산 후에 비어 있는 비트에 0을 채워 결과값을 만든다.

산술시프트 연산자는 시프트 연산 결과 값의 자료형에 따라 비어 있는 비트를 채우는 방법이 달라진다.

산술시프트 연산의 결과 값이 unsigned인 경우에는 비어 있는 비트에 0이 채워지며, 반면에 결과 값이 signed인 경우에는 비어 있는 비트에 좌측 피연산자의 MSB(부호)가 채워진다.

결합 및 반복 연산자

{4'b0011, {3{4'b0101}}}
// 16'b0011_0101_0101_0101과 동일하다.

 

게이트 수준 모델링

기본 논리 게이트 프리미티브

기본 논리 게이트 프리미티브는 두 개 이상의 입력과 하나의 출력을 가진다.

게이트 프리미티브 인스턴스의 배열

module nor_gate_4b (
	input [3:0] a,
	input [3:0] b,
	output [3:0] 
	);
	nor U0 [3:0] (y, a, b);
endmodule
// 4개의 nor 게이트 회로가 생성된다./

할당문

연속 할당문

연속 할당문은 assign 문을 이용해서 net에 스칼라(단일 비트) 또는 벡터 형태의 값을 할당하며, 논리식으로 표현된 조합회로를 모델링하기 위해 사용될 수 있다.

연속 할당문은 문장의 실행에 의해서가 아니라 할당문 우변 수식의 값 변화(event 발생)에 의해 좌변의 net에 값이 할당되는 하드웨어적인 특성을 갖는다.

절차형 할당문

절차형 할당문은 우변 수식의 event 발생과는 무관하게 해당 문장의 실행에 의해 좌변 variable에 값이 할당되는 소프트웨어적인 특성을 갖는다.

blocking 할당문 : ‘=’

nonblocking 할당문 : ‘≤’

Verilog HDL 소스코드 내에서 연속 할당문의 순서는 시뮬레이션 결과에 영향을 미치지 않으나, always, initial 블록 내에서 절차형 할당문 좌변은 net가 될 수 없으며, 반드시 reg, integer, real, time, realtime 중의 하나로 선언되어야 한다.


행위수준 모델링

always 구문과 initial 구문

always 구문

always 구문은 행위수준 모델링에 사용되는 대표적인 구문이며, 논리합성이 지원된다.

always 구문은 시뮬레이션이 실행되는 동안 반복적으로 실행되며, 타이밍 제어와 연관된 표현에 유용하게 사용될 수 있다.

! always 내부에서 절차형 할당문 좌변의 변수는 reg 자료형으로 선언되어야 한다.

! 설계자의 실수로 일부 신호가 감지신호목록에서 빠지면, 합성 전의 RTL 시뮬레이션 결과와 합성 후의 시뮬레이션 결과가 다를 수 있다.

initial 구문

시뮬레이션이 실행되는 동안 한 번만 실행된다.

initial 구문의 begin-end 블록은 절차형 문장들로 구성되며, 절차형 문장들은 나열된 순서가 실행 결과에 영향을 미치게 된다.

initial 구문은 논리합성이 지원되지 않으므로 시뮬레이션을 위한 테스트벤치에 사용된다.

절차형 할당문

- 연속 할당 : 입력 피연산자의 값에 변화가 발생할 때마다 우변의 식이 평가되고, 그 결과 값이 할당문 좌변의 net를 구동하는 하드웨어적 특성을 가진다.

- 절차형 할당 : 문장이 나열된 순서대로 실행되어 할당문 좌변의 변수 값을 갱신하는 소프트웨어적 특성을 가진다.

blocking 할당문(=)

현재 할당문의 실행이 완료된 이후에 그 다음의 할당문이 실행되는 순차적 흐름을 갖는다.

nonblocking 할당문(≤)

being-end 블록 내의 할당문들은 우변이 동시에 평가된 후, 문장의 나열 순서 또는 지정된 지연 값에 따른 할당 스케줄에 의해 좌변의 변수에 값이 갱신된다.

동일 시점에서 변수들의 순서나 상호 의존성에 의해 할당이 이루어져야 하는 경우에 사용된다.

if 조건문

if 문 내부의 else문은 이전의 가장 가까운 if와 결합된다는 원칙이 일반적으로 적용된다.

if와 else의 관계를 명확하게 나타내기 위해서는 begin-end 블록을 사용하는 것을 권장한다.

case 문

모든 조건식의 비트 크기가 일치해야 비트 단위의 정확한 일치가 판단될 수 있으며, 따라서 case 문의 조건식과 모든 case-item의 비트 크기는 큰 쪽에 맞추어져야 한다. case 조건식 비교에서 x와 z값을 포함해서 비교하는 이유는 x와 z를 감지하고 x와 z에 의해 발생될 수 있는 문제점들을 시뮬레이션으로 확인하기 위함이다.

절차형 할당의 타이밍 제어

절차형 할당의 실행을 제어하기 위해 두 가지 형태의 타이밍 제어 방법을 제공한다.

지연, 이벤트 제어 두가지가 있다.

시뮬레이션 타이밍의 제어를 위해 다음의 세 가지 방법 중 하나를 사용할 수 있다.

1. 기호 #으로 지정되는 지연

2. 기호 @으로 지정되는 이벤트 제어

3. while 루프와 이벤트 제어의 조합에 함께 사용되는 wait 문

지연 제어

지연을 갖는 절차형 할당문은 지정된 지연 값만큼 그 문장의 실행이 지연된다.

지연 값이 x 또는 x 값을 갖는 경우에는 지연이 0으로 처리된다.

이벤트 제어

절차적 할당문은 net나 variable의 값 변화 또는 선언된 이벤트의 발생에 동기화되어 실행될 수 있다.

net나 variable의 값 변화는 순차문의 실행을 트리거하기 위한 이벤트로 사용될 수 있으며, 이를 함축적 이벤트 감지라 한다.

이벤트는 값의 변화 방향을 기반으로 검출될 수도 있으며, 값이 1로 변하는 posedge(상승에지) 이벤트, 값이 0으로 변하는 negedge(하강에지) 이벤트가 이에 속한다.

블록문

블록문은 2개 이상의 문장을 그룹으로 묶어 구문적으로 하나의 문장처럼 처리되도록 하며, Verilog HDL에는 다음과 같은 두 가지 형태의 블록문을 제공한다.

- begin - end : 절차형 할당문 블록

- fork - join : 병렬문 블록


구조적 모델링

모듈

모듈 포트의 자료형 규칙

  입력 포트 출력 포트 입출력(inout) 포트
모듈 인스턴스(모듈 외부) net 또는 variable 자료형 (real 자료형은 예외) net 자료형만 가능 net 자료형만 가능
모듈 정의(모듈 내부) net 자료형만 가능 net 또는 variable 자료형 (real 자료형은 예외) net 자료형만 가능

 

모듈 인스턴스

parameter, defparam

module dff (output q,
	input clk, input d);
	
	paramter delay = 1;
	reg q;
	
	always @ (posedge clk) begin
		#delay q <= d;
	end
	
endmodule

//-------------------------------

module reg_5b (output [4:0] qout,
	input clk, input [4:0] din);
	
	dff u1 (qout[0], clk, din[0]);
	dff u2 (.clk(clk), .q(qout[1]), .d(din[1]);
	dff u3 (qout[2], clk, din[2]);
	defparam u3.delay = 2.5; //defparam문으로 파라미터 delay를 2.5로 변경
	dff #(3) u4 (qout[3], clk, din[3]); //인스턴스 u4는 순서에 의한 파라미터 값 변경이 적용
	dff #(.delay(4)) u5 (qout[4], clk, din[4]); // u5는 이름에 의한 파라미터 값 변경이 적용

endmodule

 

모듈 파라미터

모듈 parameter 값의 변경 예

module mod_param (a, b);
	real r1, r2;
    parameter [2:0] A = 3'h2;
    parameter 		B = 2;
    
    initial begin
    	r1 = A;
        r2 = B;
        $display("r1 is %f, r2 is %f", r1, r2)'
    end
endmodule
module mod_defparam;
	wire a, b;
    defparam U0.A = 3.1415;
    defparam U0.B = 3.1415;
    
	mod_param U0 (a, b);
endmodule

 

defparam문에 의한 parameter 값 변경

module vdff (
	input clk,
    input [size-1:0] in,
    output reg [size-1:0] out
    );
    
    parameter size = 1, delay = 1;
    
    always @(posedge clk)
    	#delay out <= in;
endmodule
module top(
	input clk,
    input [4:0] in1,
    input [9:0] in2,
    output [4:0] out1,
    output [9:0] out2
    );
    
    defparam
    	top.m1.size = 5,
        top.m1.delay = 10,
        top.m2.size = 10,
        top.m2.delay = 20;
        
    vdff m1 (clk, in1, out1);
    vdff m2 (clk, in2, out2);
endmodule

 

모듈 인스턴스의 파라미터 값 변경

순서에 의한 파라미터 값 변경

module_name #(value, value2, ...) instance_name (port_connection);

 

이름에 의한 파라미터 값 변경

module_name #(.param_name1([value1]), .param_name2([value2]), ...) instance_name (port_connection);

 

 

파라미터 값 변경과 인스턴스 배열에 의한 register 모델링

module DFF(
	input clk,
    input in,
    output reg out
    );
    
    always @(posedge clk)
    	out <= in;
endmodule
module dffn (
	input clk,
    input [BITS-1:0] d,
    output [BITS-1:0] q
    );
    
    paramter BITS = 1;
    
    DFF dff_array [BITS-1:0] (
    	.clk(clk),
        .in(d),
        out(q)
        );
endmodule
module MxN_register (
	input clk,
    input [M-1:0] in,
    output [M-1:0] out
    );
    
    parameter M = 3, N = 4;
    wire [M*(N-1):1] t;
    
    dffn #(M) p [1:N] (
    	.clk(clk),
        .d({t, in}),
        .q({out, t})
        );
endmodule

 

모듈 parameter 값 변경 예

module dff (
	input clk,
    input [size-1:0] in,
    output reg [size-1:0] out
    );
    
    parameter size = 5, delay = 1;
    
    always @(posedge clk)
    	#delay out <= in;
endmodule
module mod_param_assign (
	input clk,
    input [9:0] in_a,
    input [4:0] in_b,
    input [4:0] in_c,
    input [9:0] in_d,
    output [9:0] out_a,
    output [4:0] out_b,
    output [4:0] out_c,
    output [9:0] out_d
    );
    
    dff #(10, 15)	mod_a (clk, in_a, out_a);
    dff				mod_b (clk, in_b, out_b);
    dff #(.delay(12))	mod_c (clk, in_c, out_c);
    dff #(.delay(), .size(10))	mod_d (clk, in_d, out_d);
endmodule

 

 

* 모듈 parameter 값 변경시 호출 되는 모듈의 parameter값은 호출하는 paramter 값에 의해 값이 변경된다.

 

계층적 이름

 

계층적 이름의 예

module wave;
	reg stim1, stim2;
    
    cct a (stim1. stim2);
    
    initial begin :wave1
    	#100 fork :innerwave
        	reg hold;
        join
        #150 begin
        	stim1 = 0;
        end
    end
endmodule

module cct (input stim1, input stim2);
	mod Amod (stim1), Bmod (stim2);
endmodule

module mod (input in);
	always @(posedge in) being :keep
    	reg hold;
        hold = in;
    end
endmodule

 

 

계층적 이름 참조의 예

module a;
	integer i;
    b a_b1();
endmodule

module b;
	integer i;
    
    c b_c1(), b_c2();
    
    initial
    	#10 b_c1.i = 2;
endmodule

module c;
	integer i;
    initial begin
    	i = 1;
        b.i = 1;
    end
endmodule

module d;
	integer i;
    b d_b1();
    initial begin
    	a.i = 1;			d.i = 5;
        a.a_b1.i = 2; 		d.d_b1.i = 6;
        a.a_b1.b_c1.i = 3; 	d.d_b1.b_c1.i = 7;
        a.a_b1.b_c2.i = 4; 	d.d_b1.b_c2.i = 8;
    end
endmodule
728x90