말 그대로 숫자를 세는 카운터이다. JK플립플롭을 기반으로 하여, 이전에 설계했었던 add에 뺄셈을 할 수 있도록 설계한 카운터라고 할 수 있다. 컴퓨터의 논리구조로는 덧셈과 곱셈만 할 수 있다. 다음을 참고해보자.
//a - b를 실행하려고 할 때
a = 8
b = 10
a == 4'b1000;
b == 4'b1010;
1000(2) - 1010(2) = (-)0010(2) ...?
이상한 결과가 나오지 않는가? -가 앞에 붙는다. 또한 언급했듯이 덧셈과 곱셉만 할 수 있는 컴퓨터로써는 이런 결과를 실행할수 없다. 따라서 빼려는 b의 1의 보수를 취해 그것을 더한다면 뺄셈이 가능하다!
a = 8
b = 10
a == 4'b1000;
b == 4'b1010;
//b의 1의 보수는 0101;
1000(2) + 0101(2) = 1101(2) = 13....?
이번 결과도 이상하다. 8 + 5가 되어버렸다. 이것은 보수의 성질을 간과한 결과이다. 보수를 취했을 때, 2진수의 첫째 자리를 부호로 삼는 것이다. 또한 (1)1111을 0으로, (1)1110을 -1로 거꾸로 센다. 따라서 1/0101은 -10이다. 다시 계산하면
(0)1000(2) + (1)0101(2) = (1)1101(2) = -2
올바른 결과가 나왔다. 사람들의 인식으로는 엄청나게 쉬운 계산이지만, 컴퓨터는 이러한 복잡한 과정을 거쳐서 계산하게 된다. 이 사실을 토대로 다음과 같은 8진 카운터 감가산기를 설계하여 보았다.
module ADD_p (A, B, Cin, S, Cout);
parameter BW =[계산하고자 하는 비트 숫자];
input [BW -1:0] A, B;
input Cin;
output [BW -1:0] S;
output Cout;
wire [BW -1:0] A, B;
wire [BW -1:0] S;
wire [BW:0] tc;
genvar i;
assign tc[0] =Cin;
assign Cout =tc[BW];
generate
for (i = 0; i < BW; i = i + 1) begin : add
assign S[i] = A[i] ^ B[i] ^ tc[i];
assign tc[i + 1] = (A[i] & B[i]) | (A[i] & tc[i]) | (B[i] & tc[i]);
end
endgenerate
endmodule
A, B는 더하는 숫자, Cin은 전 자릿수에서 올라오는 carry, 올림수이다. 또한 아웃의 S는 덧셈 이후의 자릿수를 의미하며, Cout은 연산 이후의 다음 자리 숫자에 더하는 것이다. 근데 왜 쌩뚱맞게 덧셈을 하는지 궁금할 것이다. 그 이유는 다음과 같다.
module ADSB4 (x, y, s, sum, cout);
parameter BW =8;
input [BW-1:0] x, y;
input s;
output [BW-1:0] sum;
output cout;
wire [BW-1:0] x, y, ty;
wire [BW-1:0] sum;
assign ty = s ? ~y : y;
ADD_p U0 (.A(x), .B(ty), .Cin(s), .S(sum), .Cout(cout));
endmodule
ADD_p에 assign문장 하나를 더해준 감산기 ADSB4이다. 위 카운터 문단에서 언급했듯이, 빼주려는 y값을 반전시켜 1의 보수로 취함으로써 더해주면, 다음과 같은 식이 완성된다.
x + (-y)
따라서 훌륭하게 뺄셈이 완료되었다! 테스트벤치 파일을 작성하여서 감가산기의 결과를 살펴보자.
`timescale 1ns/1ps
module TB_ADSB4;
parameter BW =8;
reg [BW-1:0] x, y;
reg s;
wire [BW-1:0] sum1, sum2;
wire cout1, cout2;
ADSB4 inst_ADSB4 (.x(x), .y(y), .s(s), .sum(sum1), .cout(cout1));
ADD_p inst_ADD_p (.A(x), .B(y), .Cin(s), .S(sum2), .Cout(cout2));
initial begin
#10; x = $random(); y = $random(); s = $random();
#10; x = $random(); y = $random(); s = $random();
#10; x = $random(); y = $random(); s = $random();
#10; x = $random(); y = $random(); s = $random();
#10; x = $random(); y = $random(); s = $random();
#10; x = $random(); y = $random(); s = $random();
#10; x = $random(); y = $random(); s = $random();
#10; x = $random(); y = $random(); s = $random();
#10; x = $random(); y = $random(); s = $random();
#10; x = $random(); y = $random(); s = $random();
#10; x = $random(); y = $random(); s = $random();
end
endmodule
여러 개의 랜덤 함수를 쓰는 것은 너무 귀찮고 예쁘지도 않다. 추후 테스트벤치를 작성할 때는 for문이나 while문을 사용해서 쉽게 랜덤 함수를 출력하려고 한다.
위와 같은 예쁜 그래프가 나타났다. 그래프를 살펴보면, 연산 결과가 훌륭하게 나타나는 것을 확인해볼 수 있다.