이번 포스트는 generate와 반복문을 다룬다.
Verilog를 쓰다보면 1000개쯤 되는 reg 변수들을 초기화한다고 하는 반복적인 상황에 처할 때가 있다.
전체 코드에서 대부분을 재활용하고 나머지 부분을 용도에 따라 다르게 사용해야 하는 경우도 있다.
전자의 상황에서는 반복문을 쓰는게 쓰기도 편하고 가독성도 좋다. 후자에서는 generate를 사용하면 편하게 사용이 가능하다.
반복문의 경우 흔히 C에서 사용하는 for, while 등과 생소한 forever, repeat가 Verilog에서 사용된다.
4개 가운데 for문이 자주 사용되는데, 오늘은 이 for문을 주로 살펴볼 것이다.
합성 가능한 for문은 아래와 같은 경우에 사용한다.
1. 반복적인 병렬 assignment 구문
2. 반복적인 병렬 모듈 인스턴스 선언
아래와 같이 사용하면 된다.
module gen_ff_0(
output [5:0] Q,
input clk, rst,
input [8*6-1:0] Data
);
wire [7:0] DataChunk [0:5];
genvar i;
// generate
for (i=0;i<6;i=i+1) begin : FF_chunk
ff8b FF(DataChunk[i], clk, rst, Data[i*8 +: 8]);
end
// endgenerate
/* another assignments */
endmodule
module gen_ff_1(
output [5:0] Q,
input clk, rst,
input [8*6-1:0] Data
);
reg [7:0] DataChunk [0:5];
genvar i;
// generate
for (i=0;i<6;i=i+1) begin
always @ (posedge clk) begin
if (rst) begin
DataChunk[i] <= 8'b0;
end
else begin
DataChunk[i] <= Data[i*8 +: 8];
end
end
end
// endgenerate
/* another assignments */
endmodule
module for_assign(
output [7:0] Mux_out,
input [8*16-1:0] Data,
input [3:0] sel
); // Mux example
wire [7:0] Data_Array [0:15];
genvar i;
// generate
for (i=0;i<16;i=i+1) begin
assign Data_Array[i] = Data[i*8 +: 8];
end
// endgenerate
assign Mux_out = Data_Array[sel];
endmodule
generate는 endgenerate와 같이 쓰이며, 반복적인 assignment를 쓰는데는 굳이 필요없어서 주석처리했다.
genvar는 generate하고자 하는 assignment들을 구분하는데 쓰이는 index용 변수다.
첫번째 모듈의 for loop의 begin 옆에 ": FF_chunk"는 해당 loop에서 선언된 모듈 인스턴스에 접근하기 위한 block의 name을 선언한 것이다. block name을 선언하면 루프 내의 모듈 인스턴스를 접근할 수 있게된다. "FF_chunk[2].FF"는 DataChunk[2]를 가지고 있는 플립플롭에 접근하는 것이다.
for 루프의 begin 블록 하나당 name을 정할 수 있으며 점(.)을 통해 하위 인스턴스로 접근 가능하다.
Generate를 파라미터 조건에 따라 사용하는 방법도 있다. 서로 다른 모듈들을 조건에 따라 바꿔가며 사용하는 경우에 쓰인다. if-else-if 또는 case문에서 사용이 가능하며 아래와 같이 작성하면 된다.
module gen_if
#(parameter MODE = 0)
(
output [3:0] S,
output Cout,
input Cin,
input [3:0] A, B
);
generate
if (MODE == 0) begin : ADD4
RCA4b ADD(S, Cout, Cin, A, B);
end
else if (MODE == 1) begin : ADD4
CLA4b ADD(S, Cout, Cin, A, B);
end
endgenerate
endmodule
module gen_case
#(parameter MODE = 0)
(
output [3:0] S,
output Cout,
input Cin,
input [3:0] A, B
);
generate
case(MODE)
0: begin: ADD4
RCA4b ADD(S, Cout, Cin, A, B);
end
1: begin: ADD4
CLA4b ADD(S, Cout, Cin, A, B);
end
endcase
endgenerate
endmodule
공식 문서에서는 각 조건에서의 block name을 같은 것으로 사용하는 것으로 나오는데, 다른 이름을 사용하면 파라미터가 다른 모듈에 접근하기가 힘들기 때문인 것으로 보인다.
모듈 인스턴스를 선언할때 기존 선언과 같은 문법을 따르므로(파라미터 변경 등이 가능하다) 참고하자.
always block 안에서의 반복문도 가능하다. 차이점은 인스턴스화가 불가능하다는 것과 genvar를 사용하지 못한다는 것이다.
최근 포스트 중 하나인 dct 구현에서도 반복적인 초기화 구문 64개가 tp_mem.v에 포함되어있다. tp_mem_old와 tp_mem 간 always block을 비교하며 보면 된다.
module tp_mem
#(parameter BW = 8)
(
output [BW-1:0] mem_out,
output reg enb_out,
input [BW-1:0] mem_in,
input clk, rst, enb
);
/* ------------------------- */
integer i, j;
always @ (posedge clk) begin
if (rst) begin
for (i=0;i<8;i=i+1) begin
for (j=0;j<8;j=j+1) begin
data[j][i] <= 0;
end
end
end
else if (odd) begin
data[cnt_row][cnt_col] <= mem_in;
end
else begin
data[cnt_col][cnt_row] <= mem_in;
end
end
endmodule
module tp_mem_old
#(parameter BW = 8)
(
output [BW-1:0] mem_out,
output reg enb_out,
input [BW-1:0] mem_in,
input clk, rst, enb
);
/* ------------------------- */
always @ (posedge clk) begin
if (rst) begin
data[0][0] <= 0;
data[1][0] <= 0;
data[2][0] <= 0;
data[3][0] <= 0;
data[4][0] <= 0;
data[5][0] <= 0;
data[6][0] <= 0;
data[7][0] <= 0;
data[0][1] <= 0;
data[1][1] <= 0;
data[2][1] <= 0;
data[3][1] <= 0;
data[4][1] <= 0;
data[5][1] <= 0;
data[6][1] <= 0;
data[7][1] <= 0;
data[0][2] <= 0;
data[1][2] <= 0;
data[2][2] <= 0;
data[3][2] <= 0;
data[4][2] <= 0;
data[5][2] <= 0;
data[6][2] <= 0;
data[7][2] <= 0;
data[0][3] <= 0;
data[1][3] <= 0;
data[2][3] <= 0;
data[3][3] <= 0;
data[4][3] <= 0;
data[5][3] <= 0;
data[6][3] <= 0;
data[7][3] <= 0;
data[0][4] <= 0;
data[1][4] <= 0;
data[2][4] <= 0;
data[3][4] <= 0;
data[4][4] <= 0;
data[5][4] <= 0;
data[6][4] <= 0;
data[7][4] <= 0;
data[0][5] <= 0;
data[1][5] <= 0;
data[2][5] <= 0;
data[3][5] <= 0;
data[4][5] <= 0;
data[5][5] <= 0;
data[6][5] <= 0;
data[7][5] <= 0;
data[0][6] <= 0;
data[1][6] <= 0;
data[2][6] <= 0;
data[3][6] <= 0;
data[4][6] <= 0;
data[5][6] <= 0;
data[6][6] <= 0;
data[7][6] <= 0;
data[0][7] <= 0;
data[1][7] <= 0;
data[2][7] <= 0;
data[3][7] <= 0;
data[4][7] <= 0;
data[5][7] <= 0;
data[6][7] <= 0;
data[7][7] <= 0;
end
else if (odd) begin
data[cnt_row][cnt_col] <= mem_in;
end
else begin
data[cnt_col][cnt_row] <= mem_in;
end
end
endmodule
두 always block은 동일하게 동작한다. 주의할 점은 always 내부 루프에는 genvar 대신 integer를 써야한다는 것이다. genvar를 사용하면 컴파일 단계에서부터 에러가 난다.
공식 문서 12.4 항 Genarate constructs을 참고하면 된다.
'Verilog HDL 설계' 카테고리의 다른 글
32비트 부동소수점 곱셈 구현 (1) | 2024.01.02 |
---|---|
Tree Multiplier(16bit Dadda Multiplier) (0) | 2023.02.24 |
4bit/16bit Carry Lookahead Adder 설계 (0) | 2023.02.22 |
Icarus Verilog 사용법 및 유용한 팁 (0) | 2022.09.12 |
Single Cycle RISC-V 32I 프로세서 설계 (6) | 2022.07.14 |