sv(system verilog)의 가장 큰 장점은 c++와 언어의 형식이 매우 비슷하다는 것이다. verilog도 C 기반이지만, 코딩 초보자이던 나는 정말 헷갈렸다.
하드웨어적인 이해가 필요한 언어는 뭔가 보여주고, 구현하는 등의 굵직하고 이미 라이브러리가 완성된 C에 비하여, 미세한 테크닉이 필요한 verilog를 작성할 때 더 많은 노력과 주의를 요하는 것은 너무 어려운 일종의 벽이였었다.
sv는 생각보다 너무 적은 정보가 공개되어 있다. verilog가 반도체 컴퓨팅/시뮬레이팅에만 국한되어 사용되어지기 때문에, 그것을 개수한 sv는 더더욱 정보가 제한되어 있다. 때문에, 대량의 라이브러리를 구하는 것은 커녕 구성하는 것에도 한계가 존재하기에, 일반적인 CPU 구조를 구현하는 것보다 비교적 간단한 차세대 구조인 RISC-V 구조를 구현해보기로 하였다.
RISC-V(reduced instruction set computer)는 이름과 같이 명령어를 줄여서 간단한 명령어 셋을 구성하면서 복잡성을 감소시키는 대신, 프로그램을 실행하기 위해 Instruction을 많이 실행해야 하는 단점을 가지지만 실제 실행 사이클을 매우 줄이므로 더 효율적인 구조라고 할 수 있다.
하지만 CISC(complex instruction set conputer)는 단일 카테고리 거대 라이브러리를 구성하여 대부분의 컴퓨터 구조에서 사용되므로, 응용 프로그램이 이미 수없이 많이 존재하고 대중화되어 접근성이 높으나, RISC는 신생 구조이기 때문에 제각각 구조가 모두 다르며, 따라서 접근성과 자료가 비교적 적다는 단점이 존재한다.
RISC가 낮선 만큼 하루에 하나씩 모듈을 구현해보는 것으로 설계를 시작해 볼 것이다. 또한 구현은 verilog로 모듈을 구성하되, 시뮬레이션은 sv를 사용하여 검증할 것이다.
`timescale 1ns / 1ps
module SV_regfile_32by32 #(parameter BW = 32, parameter DW = 32)
(
input clk, rst,
input [BW - 1:0] data_in,
input rw,
input [clog2(BW) - 1:0] DR,
input [clog2(BW) - 1:0] DA,
input [clog2(BW) - 1:0] DB,
output reg [BW - 1:0] data_out1,
output reg [BW - 1:0] data_out2
);
reg [BW - 1:0] regfile [DW - 1:0];
integer i;
always @ (posedge clk or negedge rst)
begin
if (!rst)
begin
for(i = 0 ; i < DW; i = i + 1)
begin
regfile[i] <= 32'b0;
end
end
else
begin
data_out1 <= regfile[DA];
data_out2 <= regfile[DB];
if (rw)
begin
regfile[DR] <= data_in;
end
else
begin
regfile[DR] <= regfile[DR];
end
end
end
function integer clog2;
input integer value;
integer temp;
begin
temp = value - 1;
for (clog2 = 0; temp > 0; clog2 = clog2 + 1)
begin
temp = temp >> 1;
end
end
endfunction
endmodule
`timescale 1ns / 1ps
module TB_regfile_32by32;
parameter BW = 32;
parameter DW = 32;
reg clk, rst;
reg [BW - 1:0] data_in;
reg rw;
reg [clog2(BW) - 1:0] DR;
reg [clog2(BW) - 1:0] DA;
reg [clog2(BW) - 1:0] DB;
reg [BW - 1:0] data_out1;
reg [BW - 1:0] data_out2;
integer i;
SV_regfile_32by32
inst_SV_regfile_32by32
(
clk, rst,
data_in,
rw,
DR,
DA,
DB,
data_out1,
data_out2
);
initial begin
DR = 5'd0;
DA = 5'd0;
DB = 5'd0;
clk = 1'b0;
rst = 1'b1;
rw = 1'b0;
#10; rst = 1'b0; #10; rst = 1'b1;
end
always #5 clk = ~clk;
initial begin
for (i = 0; i < BW; i = i + 1)
begin
#10;
data_in <= $urandom;
DR <= DR + 1'b1;
rw = ~rw;
end
#100;
DR = 0;
rw = 1'b0;
for(i = 0; i < BW; i = i + 1)
begin
#10;
DA <= $urandom;
DB <= $urandom;
end
end
function integer clog2;
input integer value;
integer temp;
begin
temp = value - 1;
for (clog2 = 0; temp > 0; clog2 = clog2 + 1)
begin
temp = temp >> 1;
end
end
endfunction
endmodule
$urandom 명령어를 이용하여 unsigned uint32_t형 자료형을 임의로 삽입하였다. sv의 위력이 느껴지는 output이다.
add를 바꾸어가며 출력한 내용이다. x 데이터가 번갈아 출력되는 이유는 첫번째 그래프 시기에서 rw en를 0 1를 번갈아 입력했기 때문이다. rw가 없을 경우, 입력되지 않게 말이다.
정상적으로 입력된 데이터가 출력되는 것을 볼 수 있다.
DR로 입력할 때, wr를 홀수 번 주소에만 on 했었다. 이 경우, 홀수는 입력된 데이터가 출력될 것이고, 짝수 번의 데이터는 32'h00000000를 나타냄을 알 수 있으므로, wr의 enable 효과와 DR로 입력해낸 효과 두 가지 모두를 달성한 것을 확인할 수 있다.