[2024.04.12.금] 인천인력개발원 하만 세미콘 아카데미
Verilog를 이용한 RTL 시스템 반도체 설계
시스템 작업(System Tasks)
1. System Tasks: 미리 작성된 코드의 모음
- Simulation Tasks: 가시성과 디버깅 향상
- Operations: 콘솔에 출력 / net의 값 모니터링 / 시뮬레이션 시작,정지
- $로 시작
- Verilog에서 타이밍 체크
2. System Tasks 예시
- $display: 콘솔에 값 출력
- ex. $display("At time %d, q = %b", $time, q);
- $time, $realtime: 현재의 시뮬레이션 시간을 정수로 표현
- $wirte: $display와 유사하나 새로운 줄을 자동으로 추가 x
- $write ("Simulation time is %t", $time);
- $strobe: 현재 시뮬레이션 사이클의 끝 부분의 값 출력
- ex. $strobe("At time %d, q = %b", $time, q);
- $monitor: 값이 변화했을 때만 모니터링, 출력
- $monitor ("time = %t, clk = %b, d = %b, q = %b", $time, clk, d, q);
- $monitoron: 모니터링 시작
- $monitoroff: 모니터링 종료
- $stop: 조건이 FALSE면 정지
- $finish: 시뮬레이션 정지
- $signed: 부호가 있는 변수로 변환
- $unsigned: 부호가 없는 변수로 변환
3. 데이터 형식
- %h / %H: Hexadecimal
- %b / %B: Binary
- %d / %D: Decimal
- $s / $S: String
- $c / $C: ASCHII Character
- %t / %T: Time
- %o / %O: Octal
- %v / %V: Strengths%e / %E: Real w/ Scientific Notification
- %f / %F: Real w/ Decimal
- %m / %M: Hierarchical Name
Verilog Subprograms
1. Subprograms vs. Modules
- Subprograms는 모듈 내외에서 모두 형성 가능하나 모듈은 불가능
- 모듈은 순차적 구조만 가짐
- Functions, Tasks는 Subprograms
- Subprograms는 유연성과 기능성이 우수
2. Tasks & Functions
- Tasks
- 다른 Task와 Function 모두 활성화 가능
- 값 반환 x
- input, output, inout parameters로 구성
- non-zero time에 시뮬레이션
- blocking(<=) & non-blocking(=) statements 허용 (blocking & non-blocking statements: 14일차 게시물 참조)
- delay, event, timing control 구문 포함
- Function
- 다른 Function만 활성화 가능
- 하나의 값 반환
- 한 개 이상의 input만 필요
- 한 개의 시뮬레이션 시간 단위로 실행
- blocking statment만 사용
- delay, event, timing control구문 사용 불가
- input에 따라 작동하며 하나의 값 반환
Verilog Functions
1. Functions 개요
- Ports
- Inputs Ports만 존재
- 최소 한 개의 Input 필요
- Returns: 한 개의 값 반환
- Timing & Simulation
- @posedge, #timing delays는 사용 불가
2. Function 구조
- keyword 'function': function을 선언하기 위해 키워드로 function 사용
- identifier: function의 이름을 정의하기 위해 키워드 function 옆에 이름 정의
- function~endfunction 내부에 함수 정의 및 반환값 정의
3. Function 종류
- Constant Functions
- Signed Functions: 반환값으로 signed operation 가능(signed 키워드 사용)
Verilog Tasks
1. Tasks 개요
- 선언: arguments는 선택사항
- 반환값 x
- Timing delay 발생 가능 - non-zero time에 시뮬레이션
2. Task 구조
- keyword 'task': task를 선언하기 위해 키워드로 task 사용
- identifier: task의 이름을 정의하기 위해 키워드 task 옆에 이름 정의
- task~endtask 내부에 지역변수 정의 및 내용 입력
Verilog Compiler Directives
1. Compiler 지시자 개요
- 지시어 컴파일
- `를 사용하여 수행
- 예시: `include, `timescale, `define, `ifdef, `else, `elseif, `endif, `ifndef, `resetall
2. 지시자 종류
- `include
- module의 파일 출력
- 디자인 일관성을 위해 헤더파일 생성
- 컴파일 실행 중 모든 소스파일을 다른 파일에 삽입
- 장점: 일반적인 정의 및 tasks 포함 -> 유지보수 용이
- `timescale
- 지연시간 및 시뮬레이션 resolution 지정
- `timescale <reference time unit> / <resolution>
- `define & `undef
- `define: text 대체 매크로 형성
- `define은 파일간 수행 가능
- 매크로 수정에 쉬운 접근
- `undef는 `define에 의해 생성된 텍스트 매크로를 제거
- 예시
- `define WIDTH 4
- `define BYTE_SIZE 8
- `undef WIDTH
Verilog Parameter
- Parameter: 변경 가능한 상수를 선언하는 과정
- 선언 예시:
parameter WIDTH = 8;
parameter MEM_ADDR = 4; - local parameter: module-locap parameter 값 보호
- Prameter vs. Local Parameter
- parameter 변경시 사용 코드
- ex1) mem #(.WIDTH(16), .MEM_ADDR(8)) I0(..<port map>);
- 위 코드로 상단에서 선언한 WIDTH와 MEM_ADDR 파라미터의 값을 각각 16, 8로 변경 가능
- #과 .은 Parameter의 값을 변경하기 위한 기호
- ex2) defparam I0.WIDTH = 16;
- 위 코드에서는 I0포트에 할당된 파라미터 WIDTH값을 defparam 키워드로 변경(아래 코드에서는 I0대신 uut로 선언하였으므로 uut.Width=~~;로 변경 가능)
실습 1: Parity Function 설계
교재 262p 참조
[설계 요구사항]
- function subprogram을 사용하여 parity function 작성
- testbench 코드 작성
- repeat문 사용, 2256번 반복 입력, d_word 생성
- $display system task를 사용하여 data와 parity를 화면에 출력
- $finish system task를 사용하여 simulation 종료
[설계 참고사항]
d_word는 10ns마다 생성
1. my_parity 프로젝트 생성
2. input으로 d_word를 버스로 31:0. output을 data_frame으로 32:0으로 선언 후 코드 작성
`timescale 1ns / 1ps
module my_parity #(parameter width = 32)( //.v 파일에서 parameter 선언 시 module 아래에서 선언
input [width-1:0] d_word,
output [width:0] data_frame );
function parity;
input [width -1:0] bv;
reg par;
integer i;
begin //fuction
par = 1'b0;
for(i = 0; i <= width; i=i+1)
par = par ^ bv[i];
parity = par; //return
end
endfunction
assign data_frame = {d_word, parity(d_word)};
endmodule
3. testbench 파일 생성을 위해 Add Source에서 Add or create simulation sources - my_parity_tb.v파일 생성(input, ouput 정의x)

4. my_parity_tb.v 코드 작성
my_parity #(.width(8)) uut (~~);는 parity에서 선언된 parameter width의 값을 8로 변경하기 위한 코드로, 상단의 parameter 변경 방법 중 ex1의 방법을 사용한 것 -> 상단의 예시에서 I0의 포트로 선언한 것을 아래 코드에서는 uut(unit under test)로 선언
`timescale 1ns / 1ps
module my_parity_tb();
parameter width = 8; //testbench 파일에서도 module 아래에서 parameter 선언
reg [width-1:0] data;
wire [width:0] dout;
my_parity #(.width(8)) //testbench를 실행 시 width의 값을 8로 변경(my_parity에서 32이던 width값을 변경)
//위에서 #은 delay가 아닌 값 변경을 위한 기호
uut( //부품번호 uut
.d_word (data),
.data_frame (dout)
);
initial begin
data = 32'd0;
repeat ( 256 )
begin
#10;
$display ("data = %b parity %b", data, dout[0]);
data = data + 1;
end
end
initial begin
#(10+256);
$finish;
end
endmodule
5. Run Simulation: console에 parity 표현됨을 확인


data = 00000000 parity 0
data = 00000001 parity 1
data = 00000010 parity 1
data = 00000011 parity 0
data = 00000100 parity 1
data = 00000101 parity 0
data = 00000110 parity 0
data = 00000111 parity 1
data = 00001000 parity 1
data = 00001001 parity 0
data = 00001010 parity 0
data = 00001011 parity 1
data = 00001100 parity 0
data = 00001101 parity 1
data = 00001110 parity 1
data = 00001111 parity 0
data = 00010000 parity 1
data = 00010001 parity 0
data = 00010010 parity 0
data = 00010011 parity 1
data = 00010100 parity 0
data = 00010101 parity 1
data = 00010110 parity 1
data = 00010111 parity 0
data = 00011000 parity 0
data = 00011001 parity 1
실습 2: Constant Functions 설계
교재 264p 참조
[설계 요구사항]
- function subprogram을 사용하여 constant function 작성
- testbench 코드 작성
- constant_fun module을 8번 instantiation
- parameter DEPTH값을 4, 8, 16, 32, 64,...로 변경
1. my_const_fun 프로젝트 생성
2. Add or Create Design Sources로 address, rd_vr, cs, data 생성

3. 코드 작성
`timescale 1ns / 1ps
module my_const_fun( rd_wr, cs, address, data );
parameter DEPTH = 512;
parameter DATA_WIDTH = 16;
localparam ADDR_WIDTH = clogb2 (DEPTH);
input rd_wr, cs;
input [ADDR_WIDTH -1 : 0] address;
inout [DATA_WIDTH -1 : 0] data;
function integer clogb2 (input integer depth); //memory 크기에 따라 필요한 address 사이즈를 계산하는 함수(2^(address size-1)= memory size)
begin
for (clogb2 = 0; depth > 0; clogb2 = clogb2 + 1)
depth = depth >> 1;
end
endfunction
initial
$display (" Parameter Values DATA WIDTH = %d; DEPTH = %d; ADDRESS WIDTH = %d ", DATA_WIDTH, DEPTH, ADDR_WIDTH);
endmodule
4. testbench를 위해 Add Source - Create simulation sources - Create File - my_const_fun_tb.v 생성 후 코드 작성
`timescale 1ns / 1ps
module my_const_fun_tb( );
//DEPTH: 메모리 사이즈
wire [15:0] data;
my_const_fun #(.DEPTH(4)) uut0(
.rd_wr(1'b0), //my_const_fun의 rd_wr의 값을 1'b0으로 변경
.cs(1'b0), //my_const_fun의 cs의 값을 1'b1으로 변경
.address(32'd1),//my_const_fun의 address의 값을 16'd0으로 변경
.data(data) //my_const_fun의 data의 값을 16'd0으로 변경
); //위 과정은 메모리에 16비트 데이터를 저장하는 코드
my_const_fun #(.DEPTH(8)) uut1( .rd_wr(1'b0), .cs(1'b0), .address(32'd1), .data(data) );
my_const_fun #(.DEPTH(16)) uut2( .rd_wr(1'b0), .cs(1'b0), .address(32'd1), .data(data) );
my_const_fun #(.DEPTH(32)) uut3( .rd_wr(1'b0), .cs(1'b0), .address(32'd1), .data(data) );
my_const_fun #(.DEPTH(64)) uut4( .rd_wr(1'b0), .cs(1'b0), .address(32'd1), .data(data) );
my_const_fun #(.DEPTH(128)) uut5( .rd_wr(1'b0), .cs(1'b0), .address(32'd1), .data(data) );
endmodule
5. Run Behavioral Simulation 후 Console 창 확인

실습 3: Two's Complement task 설계
교재 263p 참조 다음 부분을 Task로 변환

[설계 요구사항]
- task subprogram을 사용하여 2의 보수 task 작성
- 입력 data [7:0]
- 출력 tc_data [7:0[
- testbench 코드 작성
- for문 사용하여 256번 반복 입력 data 생성
- $display system task를 사용하여 data와 tc_data를 화면에 출력
- $finish문을 사용하여 simulation 종료
[설계 참고사항[
- data는 10ns마다 생성
1. my_task 프로젝트 생성
2. Add or Create Design Sources를 이용해 my_task.v를 생성하고 [3:0]의 DIN과 [3:0]의 DOUT 선언 후 코드 작성
`timescale 1ns / 1ps
module my_task(
input [3:0] DIN,
input [3:0] DOUT
);
task comp_2 (input [3:0] in_bus, output reg [3:0] comp2_bus);
reg [3:0] bus_i;
begin
bus_i = -in_bus;
comp2_bus = bus_i+1;
end
endtask
reg[3:0] com2_data;
assign DOUT = com2_data;
always @(DIN)
comp_2(DIN, com2_data);
endmodule
3. Add or Create Simulataion Sources를 이용해 my_task_tb.v를 생성하고 코드 작성
`timescale 1ns / 1ps
module my_task_tb( );
reg [3:0] data;
wire [3:0] twos_data;
integer i;
my_task uut( .DIN(data), .DOUT(twos_data));
initial begin
data = 0;
for(i = 0; i < 256; i = i+1)
begin
#10;
$display(" input data: %b, Two's Complement: %b", data, twos_data);
data = data + 1;
end
end
initial begin
#(10+256)
$finish;
end
endmodule
4. Simulation 후 Console창에 내용 출력 확인
