오늘은 Verilog로 D Flip-Flop과 Register를 만들어 보겠습니다.
- IDE: Intel Quartus 18.1
- Device: Cyclone V 5CSXFC6D6F31C6(밑에서 6번째)
- Simulation: ModelSim-Altera, Verilog HDL
목차
1. D Flip-Flop
1.1. _dff_r.v
1.2. tb_dff_r.v
1.3. simulation\modelsim\tv_dff_r.tv
1.4. RTL View
1.5. RTL Simulation
2. Register(D Flip-Flop을 instantiation하여 구현)
2.1. _register8_dff.v
2.2. _register32_dff.v
2.3. tb_register32_dff.v
2.4. simulation\modelsim\tv_register32.tv
2.5. RTL View
2.6. RTL Simulation
2.7. Flow Summary
3. Register(reg 변수를 사용하여 구현)
3.1. _register32_reg.v
3.2. tb_register32_reg.v
3.3. simulation\modelsim\tv_register32.tv
3.4. RTL View
3.5. RTL Simulation
3.6. Flow Summary
1. D Flip-Flop
clk와 reset_n이 있는 D Flip-Flop을 만들어 보겠습니다.
clk는 클럭입니다. clk의 rising edge(posedge)에 q=d가 됩니다.
reset_n은 active low로 작동합니다. 즉, reset_n==0이면 d의 값에 상관없이 q=0이 됩니다.
- Project Name & the name of Top-level: _dff_r
1.1. _dff_r.v
module _dff_r(clk, reset_n, d, q);
input clk, reset_n, d;
output reg q;
always @ (posedge clk or negedge reset_n)
begin
if(reset_n==0) q<=0;
else q<=d;
end
endmodule
[Line 5]
always @ (posedge clk or negedge reset_n)
clk가 0→1 상승하거나 reset_n이 1→0 하강할 때 begin과 end 사이의 내용을 반복합니다.
[Line 7]
if(reset_n==0) q<=0;
reset_n==0이면 즉시 q<=0;을 수행합니다.
<=는 non-blocking assignment입니다.
[Line 8]
else q<=d;
reset_n!=0이면(즉, reset_n==1이면) q<=d;를 수행합니다.
1.2. tb_dff_r.v
testvector를 사용하겠습니다.
`timescale 1ns/100ps
module tb_dff_r();
reg clk, reset;
reg reset_n, d, q_expected;
wire q;
reg [31:0] vectornum, errors; //bookkeeping variables
reg [31:0] testvectors[10000:0]; //array of testvectors
//instantiate device under test
_dff_r dut(clk, reset_n, d, q);
//generate clock
always
begin
clk=1; #5; clk=0; #5;
end
//at start of test, load vectors and pulse reset
initial
begin
$readmemb("./tv_dff_r.tv", testvectors); //Put testvector file at simulation\modelsim
vectornum=0; errors=0;
reset=1; #27; reset=0;
end
//apply test vectors on rising edge of clk
always @(posedge clk)
begin
#2; {reset_n, d, q_expected}=testvectors[vectornum];
end
//check results on falling edge of clk
always @(negedge clk)
if(~reset) begin //skip during reset==1
#6;
if(q!==q_expected) begin
$display("Error: inputs=%b", {reset_n, d});
$display(" outputs=%b (%b expected)", q, q_expected);
errors=errors+1;
end
//increment array index and read next testvector
vectornum=vectornum+1;
if(testvectors[vectornum]===32'bx) begin
$display("%d tests completed with %d errors.", vectornum, errors);
$finish;
end
end
endmodule
[Line 1~2]
clk는 testbench와 D Flip-Flop에서 공용으로 사용하겠습니다.
reset은 testbench에서 단독으로 사용하겠습니다.
reset_n은 D Flip-Flop에서 단독으로 사용하겠습니다.
[Line 30]
#2; {reset_n, d, q_expected}=testvectors[vectornum];
#2;를 쓰지 않으면 D Flip-Flop이 다음 클럭에 작동하므로 #2;를 써서 지연 시간을 줄였습니다. #1이 아니고 하필 #2;인 이유는 36행에서 negedge clk에 #6;이 붙는 것이 겹치지 않게 하기 위해서입니다.
[Line 36]
#6;
clk의 rising edge(posedge)가 일어난 후 값을 측정하도록 합니다.
Testbench를 작성했으면 Simulation 설정에서 추가해 주어야 합니다. 여기서는 설명하지 않겠습니다. Testbench를 Intel Quartus에서 추가하는 방법을 모르신다면 Intel Quartus에서 Verilog testbench 사용하기 2번 문단을 참고하시길 바랍니다.
1.3. simulation\modelsim\tv_dff_r.tv
00_0
01_0
00_0
01_0
10_0
11_1
10_0
11_1
00_0
01_0
00_0
01_0
10_0
11_1
10_0
11_1
각 줄이 <reset_n><d>_<q_expected>입니다.
예) 10_0은 reset_n=1, d=0, q_expected=0을 의미합니다.
1.4. RTL View
D Flip-Flop이 그려져 있고, D에 d가, CLK에 clk가, Q에 q가 연결되어 있으며, CLRN에 reset_n이 invert되어 들어가는 것으로 되어 있습니다.
1.5. RTL Simulation
vectornum과 errors는 10진수(Decimal)로 표시하도록 설정했습니다.
q 값의 변화는 clk의 rising edge(posedge)에만 일어나는 것을 보실 수 있습니다.
2. Register(D Flip-Flop을 instantiation하여 구현)
Register는 D Flip-Flop을 연결하여 만듭니다. 앞서 D Flip-Flop을 구현했으므로 그것을 instantiation하여 32-bit register를 구현해 보겠습니다.
- Project Name & the name of Top-level: _register32_dff
- Add files: _dff_r.v(1.1절 파일 활용)
2.1. _register8_dff.v
한 번에 D Flip-Flop 32개를 instantiation하는 것은 번거로우므로 8-bit register를 먼저 만들겠습니다.
module _register8(clk, reset_n, d, q);
input clk, reset_n;
input [7:0] d;
output [7:0] q;
_dff_r U0_dff_r(clk, reset_n, d[0], q[0]);
_dff_r U1_dff_r(clk, reset_n, d[1], q[1]);
_dff_r U2_dff_r(clk, reset_n, d[2], q[2]);
_dff_r U3_dff_r(clk, reset_n, d[3], q[3]);
_dff_r U4_dff_r(clk, reset_n, d[4], q[4]);
_dff_r U5_dff_r(clk, reset_n, d[5], q[5]);
_dff_r U6_dff_r(clk, reset_n, d[6], q[6]);
_dff_r U7_dff_r(clk, reset_n, d[7], q[7]);
endmodule
2.2. _register32_dff.v
8-bit register 4개를 연결하여 32-bit register를 만듭니다.
module _register32_dff(clk, reset_n, d, q);
input clk, reset_n;
input [31:0] d;
output [31:0] q;
_register8 U0_register8(clk, reset_n, d[7:0], q[7:0]);
_register8 U1_register8(clk, reset_n, d[15:8], q[15:8]);
_register8 U2_register8(clk, reset_n, d[23:16], q[23:16]);
_register8 U3_register8(clk, reset_n, d[31:24], q[31:24]);
endmodule
2.3. tb_register32_dff.v
`timescale 1ns/100ps
module tb_register32_dff();
reg clk, reset;
reg reset_n;
reg [31:0] d, q_expected;
wire [31:0] q;
reg [127:0] vectornum, errors; //bookkeeping variables
reg [127:0] testvectors[10000:0]; //array of testvectors
//instantiate device under test
_register32_dff dut(clk, reset_n, d, q);
//generate clock
always
begin
clk=1; #5; clk=0; #5;
end
//at start of test, load vectors and pulse reset
initial
begin
$readmemh("./tv_register32.tv", testvectors); //Put testvector file at simulation\modelsim
vectornum=0; errors=0;
reset=1; #27; reset=0;
end
//apply test vectors on rising edge of clk
always @(posedge clk)
begin
#2; {reset_n, d, q_expected}=testvectors[vectornum];
end
//check results on falling edge of clk
always @(negedge clk)
if(~reset) begin //skip during reset==1
#6;
if(q!==q_expected) begin
$display("Error: inputs=%b", {reset_n, d});
$display(" outputs=%b (%b expected)", q, q_expected);
errors=errors+1;
end
//increment array index and read next testvector
vectornum=vectornum+1;
if(testvectors[vectornum]===128'bx) begin
$display("%d tests completed with %d errors.", vectornum, errors);
$finish;
end
end
endmodule
[Line 23]
$readmemh(“./tv_register32.tv”, testvectors);
1.2절 tb_dff_r.v에서는 $readmemb를 썼지만, 여기 tb_register32_dff.v에서는 $readmemh를 씁니다.
$readmemb는 파일을 2진수로 읽어서 배열에 저장하고, $readmemh는 파일을 16진수로 읽어서 배열에 저장합니다.
testvector를 작성할 때 32bit를 2진수로 쓰면 너무 길기 때문에 16진수로 했습니다. 참고로 16진수 1자리는 2진수로 변환하면 4자리가 됩니다.
2.4. simulation\modelsim\tv_register32.tv
000000000_00000000
000000001_00000000
000000020_00000000
00000a003_00000000
100000000_00000000
100000001_00000001
100000020_00000020
10000a003_0000a003
000000000_00000000
000000001_00000000
000000020_00000000
00000a003_00000000
100000000_00000000
100000001_00000001
100000020_00000020
10000a003_0000a003
각 줄이 <reset_n><d>_<q_expected>입니다.
d와 q_expected는 16진수로 8자리씩 됩니다.
예) 10000a003_0000a003은 reset_n=1, d=0000a003, q=0000a003입니다.
2.5. RTL View
d가 _register8 4개로 나뉘어 들어갑니다.
_register8을 확대해 보면 이렇게 생겼습니다.
2.6. RTL Simulation
d와 q_expected와 q는 16진수(Hexadecimal)로, vectornum과 errors는 10진수(Decimal)로 표시하도록 설정했습니다.
D Flip-Flop과 마찬가지로 q 값의 변화는 clk의 rising edge(posedge)에만 일어나는 것을 보실 수 있습니다.
2.7. Flow Summary
Logic utiliazation은 9, Total registers는 32, Total pins는 66이 나오네요.
3. Register(reg 변수를 사용하여 구현)
이번에는 그냥 32-bit reg 변수를 선언하여 Register를 구현해 보겠습니다. 물론 그렇다고 해서 그냥 “reg 변수 선언, 끝!”은 아니고 input과 output, always문 등을 사용하도록 하겠습니다.
- Project Name & the name of Top-level: _register32_reg
3.1. _register32_reg.v
module _register32_reg(clk, reset_n, d, q);
input clk, reset_n;
input [31:0] d;
output reg [31:0] q;
always @ (posedge clk or negedge reset_n)
begin
if(reset_n==0) q<=0;
else q<=d;
end
endmodule
[Line 3~4]
d와 q를 각각 [31:0]으로 선언합니다.
[Line 6]
always @ (posedge clk or negedge reset_n)
clk가 0→1 상승하거나 reset_n이 1→0 하강할 때 begin과 end 사이의 내용을 반복합니다.
[Line 8]
if(reset_n==0) q<=0;
reset_n==0이면 즉시 q<=0;을 수행합니다.
[Line 9]
else q<=d;
reset_n!=0이면(즉, reset_n==1이면) q<=d;를 수행합니다.
3.2. tb_register32_reg.v
`timescale 1ns/100ps
module tb_register32_reg();
reg clk, reset;
reg reset_n;
reg [31:0] d, q_expected;
wire [31:0] q;
reg [127:0] vectornum, errors; //bookkeeping variables
reg [127:0] testvectors[10000:0]; //array of testvectors
//instantiate device under test
_register32_reg dut(clk, reset_n, d, q);
//generate clock
always
begin
clk=1; #5; clk=0; #5;
end
//at start of test, load vectors and pulse reset
initial
begin
$readmemh("./tv_register32.tv", testvectors); //Put testvector file at simulation\modelsim
vectornum=0; errors=0;
reset=1; #27; reset=0;
end
//apply test vectors on rising edge of clk
always @(posedge clk)
begin
#2; {reset_n, d, q_expected}=testvectors[vectornum];
end
//check results on falling edge of clk
always @(negedge clk)
if(~reset) begin //skip during reset==1
#6;
if(q!==q_expected) begin
$display("Error: inputs=%b", {reset_n, d});
$display(" outputs=%b (%b expected)", q, q_expected);
errors=errors+1;
end
//increment array index and read next testvector
vectornum=vectornum+1;
if(testvectors[vectornum]===128'bx) begin
$display("%d tests completed with %d errors.", vectornum, errors);
$finish;
end
end
endmodule
3.3. simulation\modelsim\tv_register32.tv
2.4절과 똑같이 했습니다. 스크롤 올리기 귀찮으신 분들을 위해 한 번 더 써 드리겠습니다.
000000000_00000000
000000001_00000000
000000020_00000000
00000a003_00000000
100000000_00000000
100000001_00000001
100000020_00000020
10000a003_0000a003
000000000_00000000
000000001_00000000
000000020_00000000
00000a003_00000000
100000000_00000000
100000001_00000001
100000020_00000020
10000a003_0000a003
3.4. RTL View
register가 사각형 하나에 그려져 있고, D에 d[31..0]이, CLK에 clk가, Q에 q[31..0]이 연결되어 있으며, CLRN에 reset_n이 invert되어 들어가는 것으로 되어 있습니다.
3.5. RTL Simulation
d와 q_expected와 q는 16진수(Hexadecimal)로, vectornum과 errors는 10진수(Decimal)로 표시하도록 설정했습니다.
q 값의 변화는 clk의 rising edge(posedge)에만 일어나는 것을 보실 수 있습니다.
3.6. Flow Summary
D Flip-Flop을 instantiation했을 때와 마찬가지로 Logic utiliazation은 9, Total registers는 32, Total pins는 66이 나옵니다.
4. 글 마무리
제 글을 읽어 주셔서 감사합니다. 다음에 만나요!
5. 참고 자료
1) David Money Harris, Sarah L. Harris. 2013. Digial Design and Computer Architecture. 2nd Edition. Elsevier Korea L.L.C.
2) @SEMICIRCUIT. 2025. “[Verilog] “=”(blocking)과 “<=”(non-blocking) 할당의 차이”, SEMICIRCUIT. (2025. 10. 18. 방문). https://semicircuit.tistory.com/199