가산기
`timescale 1ns / 1ps
module FA(
input x, y, z,
output c,s
);
wire w1,w2,w3;
xor(w1, x, y);
xor(s, w1, z);
and(w2,z,w1);
and(w3,x,y);
or(c,w2,w3);
endmodule
1비트 가산기 모듈을 상위 모듈에서 호출하여 사용합니다.
module FA_4bit(
input (3:0) x,y,
input ci,
output co,
output (3:0) s
);
wire (3:1) tc;
FA A1 (.x(x(0)), .y(y(0)), .z(ci), .c(tc(1)), .s(s(0)));
FA A2 (.x(x(1)), .y(y(1)), .z(tc(1)), .c(tc(2)), .s(s(1)));
FA A3 (.x(x(2)), .y(y(2)), .z(tc(2)), .c(tc(3)), .s(s(2)));
FA A4 (.x(x(3)), .y(y(3)), .z(tc(3)), .c(co), .s(s(3)));
endmodule
VIVADO IDE에서 RTL 분석을 한 결과는 위와 같습니다.
2. 디코더의 구조적 모델링
…
3. 래치의 구조적 모델링
래치는 클록에 관계없이 clk 신호가 1이면 동작하고, 0이되면 동작하지 않는 기억 소자의 일종이다.
플립 플롭과 구조가 비슷하며 플립 플롭과 마찬가지로 D, SR, JK 래치가 있습니다.
….
4. 파라미터
매개 변수는 모듈에서 사용되는 상수 또는 식의 이름 정의를 가리키며 모듈 인스턴스에서 재정의할 수 있습니다.
C 언어에서 #define 혹은 변수와 비슷한 역할을 한다.
다음과 같이 벡터나 배열 형식의 크기를 지정할 때 사용합니다.
5. 지연 시간
`timescale 1ns / 1ns
module gate_delay ;
reg a, b, c, d ;
wire y1 , y2 , y3 ;
and #20 (y1 , a, b) ;
or #(20 ,40 ) (y2 , a, b) ;
notif1 #(20 ,40 ,60 ) (y3 , c, d);
initial begin
a = 1 ; b = 1 ; c = 1 ; d = 1 ;
#100 a = 0 ; b = 0 ; c = 0 ; d = 1 ;
#100 a = 0 ; b = 1 ; c = 1 ; d = 0 ;
#100 a = 1 ; b = 0 ; c = 0 ; d = 0 ;
#100 a = 1'bx ; b = 1'bx ; c = 1'bx ; d = 1 ;
end endmodule
지연 시간은 시뮬레이션 신호의 시간을 변경할 수 있는 함수입니다.
실제 하드웨어에서는 장치에 의한 지연 시간이 항상 발생하므로 시뮬레이션에서 이를 반영해야 합니다.
각 장치의 지연 시간을 실제 값에 근사하여 정확한 동작을 예측할 수 있습니다.
위 코드의 시뮬레이션 결과는 다음과 같습니다.
y1은 a, b의 연산이 20ns 지연되어 표시되고, y2도 지연 시간이 20ns 이후로 표시되고, y3은 3상 버퍼를 통과한 연산의 fall 지연이 40ns 지연된 채로 됩니다.
6. 인스턴스 배열
8비트 레지스터의 2개의 구조적 모델링 비교
module dreg_a
#(parameter size = 8)
(output wire (size-1:0) q,
input (size-1:0) d,
input clk, clr);
// 인스턴스 배열
dff1 u(size-1:0) (clk, clr, d, q);
endmodule
module dreg8
(output wire (7:0) q,
input (7:0) d,
input clk, clr);
dff1 u7 (clk, clr, d(7), q(7));
dff1 u6 (clk, clr, d(6), q(6));
dff1 u5 (clk, clr, d(5), q(5));
dff1 u4 (clk, clr, d(4), q(4));
dff1 u3 (clk, clr, d(3), q(3));
dff1 u2 (clk, clr, d(2), q(2));
dff1 u1 (clk, clr, d(1), q(1));
dff1 u0 (clk, clr, d(0), q(0));
endmodule
인스턴스 배열을 사용하면 인스턴스 수가 많을 때 쉽게 호출할 수 있습니다.
7. 생성문
모듈, 프리미티브 게이트, 연속 할당, initial/always 블록 등을 하나 이상 생성
net, reg, integer 등의 데이터형을 생성영억내에서 선언 가능
인스턴스는 고유 식별자를 가지며 계층 이름으로 참조할 수 있습니다.
반복 생성 문
module xor_for
#(parameter width = 4, delay =10)
(output wire (1:width) y,
input (1:width) in1, in2);
generate
genvar i; // 반복 인덱스 변수 선언
for (i = 1; i <= width; i=i+1) begin: xi //식별자를 반드시 선언
assign #delay y(i) = in1(i) ^ in2(i);
end
endgenerate
endmodule
generate – endgenerate 구문의 for 문을 사용하여 특정 모듈 또는 블록을 반복적으로 인스턴스화합니다.
변수, 모듈, UDP, 게이트 프리미티브, initial, always 등을 인스턴스화할 수 있습니다.
생성 문에서 사용하는 변수(i)는 genvar로 선언해야 합니다.
for 문 begin 뒤에 블록 식별자(:식별자)를 붙여야 합니다.
예
module gray2bin2(bin, gray);
parameter SIZE = 4;
output (SIZE-1:0) bin;
input (SIZE-1:0) gray;
reg (SIZE-1:0) bin;
genvar i;
generate
for(i=0; i<SIZE; i=i+1) begin : gb1
always @ (gray(SIZE-1:i))
bin(i) = ^gray(SIZE-1:i);
end
endgenerate
endmodule
리플 캐리 가산기
`timescale 1ns / 1ns
module rca_for (a, b, ci, co, sum );
parameter size = 4;
input (size-1:0) a, b;
input ci;
output co;
output wire (size-1:0) sum;
wire (size:0) c;
genvar i;
assign c(0) = ci;
generate
for(i=0; i<size; i=i+1) begin : ra
wire t1, t2, t3; //로컬 net 선언
xor g1 (t1, a(i), b(i));
xor g2 (sum(i), t1, c(i));
and g3 (t2, a(i), b(i));
and g4 (t3, t1, c(i));
or g5 (c(i+1), t2, t3);
end
endgenerate
assign co = c(size);
endmodule
generate for 문은 for 내부 코드를 똑같이 생성해주는 코드이며, RTL 분석을 하면 FA가 복수 생성된 것을 볼 수 있다.
generate if
module mult_if (x, y, prod);
parameter x_wid = 8, y_wid = 8;
localparam prod_wid = x_wid + y_wid;
// localparam으로 선언된 파라미터는 defparam나 모듈 인스턴스 #에서 변경 불가
input (x_wid-1:0) x ;
input (y_wid-1:0) y;
output wire (prod_wid-1:0) prod;
generate
if( (x_wid < 8) || (y_wid < 8) )
CLA_mult #(x_wid, y_wid) u1 (x, y, prod); // CLA 승산기 인스턴스
else
WALLACE_mult #(x_wid, y_wid) u2 (x, y, prod); // Wallace-tree 승산기 인스턴스
endgenerate
endmodule
주로 장치 마법사 사용에 자주 사용됩니다.
조건이 맞으면 CLA 곱셈기를 만들고, 그렇지 않으면 wallace 곱셈기를 만듭니다.
generate case
module rca_case
#(parameter size = 4)
( input (size-1:0) a, b,
input cin,
output reg (size-1:0) sum,
output reg co, neg, ov );
reg (size-1:0) c;
generate
genvar i;
for (i = 0; i< size; i=i+1) begin: stage
case(i)
0: begin
always @(*) begin
sum(i) = a(i) ^ b(i) ^ cin;
c(i) = a(i) & b(i) | b(i) & cin | a(i) & cin;
end
end
size-1: begin
always @(*) begin
sum(i) = a(i) ^ b(i) ^ c(i-1);
co = a(i) & b(i) | b(i) & c(i-1) | a(i) & c(i-1);
neg = sum(i); // 부호
ov = co ^ c(i-1); // overflow
end
end
default: begin
always @(*) begin
sum(i) = a(i) ^ b(i) ^ c(i-1);
c(i) = a(i) & b(i) | b(i) & c(i-1) | a(i) & c(i-1);
end
end
endcase
end
endgenerate
endmodule
if 문과 마찬가지로 조건에 맞는 회로를 생성할 때 사용된다.
8. 할당문
continuous assignment
net형 객체에 값 할당
회로의 실행 상태에 관계없이 항상 유효
assignment의 입력 신호가 변경되면 출력값이 변경됩니다.
연속 할당 명령문의 예
wire mem_we;
assign mem_we = cs & wr; // 명시적 연속할당
wire mem_rd = cs & rd; // 암시적 연속할당
//지연은 FPGA에는 동작하지 않음. 시뮬레이터에 실제 소자의 지연시간을 시뮬레이션 하는 용도로만 사용
assign #10 out = in1 & in2; // 지연 연속할당
assign #12 {c, sum} = a + b + cin; // 좌변 비트 결합
assign mux_out = (sel) ? in1 : in2; // 2 input MUX
assign tc = (count == 4'h9); // 터미널 카운팅
assign high_byte = data(15:8); // 비트 슬라이스 우변
연속 할당문으로 작성된 가산기
module add_ap (a, b, res);
parameter size = 8;
input (size-1:0) a, b;
output wire (size:0) res;
assign res = a + b; // res(size)는 캐리
endmodule
연속 할당 명령문 지연
정규 지연
wire out;
assign #10 out = in1 & in2;
우변의 연산 결과가 좌변에 할당되기까지의 시간이다.
암시적 할당
wire #10 out = in1 & in2;
그물 지연
wire #10 out;
assign out = in1 & in2;
out을 사용하는 모든 드라이버에 영향을줍니다.
선언되었을 때 이미 지연이 되었기 때문이다.
procedure assignment
variable 유형 객체에 값을 할당합니다.
blocking assignment
c 언어로 프로그램을 실행하는 것처럼 작동합니다.
C = 5가 실행되면 C로 값이 직접 업데이트되므로 D는 12입니다.
none-blocking assignment
A<= B & C;
D<= A | C;
대입문의 우변인 B&C의 값의 계산은 종료했습니다만, A에 넣지 않고 다음의 문AC를 실행합니다.
A의 값은 여전히 갱신되기 전이므로, B&C가 실행되기 전의 값으로서 A|C를 계산한다.
none-blocking 할당문을 작성하는 이유는 blocking 구문을 사용할 때 순서에 따라 다른 회로를 합성할 수 있기 때문입니다.
always @(posedge clk)
b = a;
always @(posedge clk)
c = b;
always는 동시에 실행되지만, b = a의 평가가 먼저 이루어지면
입력에 a가 들어가고 클럭이 들어갈 때마다 출력에 b, c가 나오는 회로가 나올 수 있다.
반대로 c = b가 먼저 계산되면
이러한 시프트 연산기가 생성된다.
이 현상을 방지하기 위해 none-blocking을 사용하십시오.
always @(posedge clk)
b <= a;
always @(posedge clk)
c <= b;
1. clk의 라이징 엣지가 발생하면, always의 각 항의 평가가 행해진다
2. a의 값을 읽고 다른 위치에 저장하고 b의 값을 읽고 다른 위치에 잠시 보관합니다.
3. 모든 구문의 평가가 끝나면 다른 곳에 보관하고 있던 a, b 값을 취해 갱신을 실시한다.
이와 같은 동작을 행하면, always의 실행 순서에 관계없이 항상 일관된 회로가 나온다.