기존 Combinational Logic과는 다르게 Sequential Logic은 메모리 요소를 더하였다는 특징이 있다.
이때의 메모리 요소가 D-FF이 사용되는 것이고, FlipFlop에는 CLock이 사용된다.
먼저 주어진 Upcount 코드에서 Testbench를 작성해보았다
module upcount(Resetn, Clock, E, Q);
input Resetn, Clock, E;
output reg[3:0] Q;
always @(negedge Resetn or posedge Clock) // 여기에 왜 begin 없음?
if(!Resetn) begin
Q <= 0;
end
else begin
if(E) Q <= Q + 1;
end
endmodule
always 구문은 @속에 있는 port의 변화가 있을 때 block이 동작한다. 이것을 level sensitive라고 하며, edge sensitive로 변화하는 always block구문도 있는데 괄호 속에 있는 input port앞에 postedge 혹은 negedge를 붙여서 edge에 따라 always 구문이 실행되는 것이다.
CLK이라는 시그널이 따로 존재하는 것이 아니라, always 조건문 안에 CLK이 들어감으로써 비로소 CLK으로 작동할 수 있는 것이다.
+ 추가 의문점
D-FF asynchronous reset 코드에서는 always 조건문에 posedge Clock과 negedge Reset이 들어왔었는데, 왜 독립적이라고 하는지? Reset신호는 Clock신호와 무관한 것인지?
module flipflop(D, Clock, Resetn, Q);
input D, Clock, Resetn;
output ref Q;
always@(negedge Resetn opr posedge Clock)
if(!Resetn) Q<=0;
else Q<=D;
endmodule
module flipflop(D, Clock, Resetn, Q);
input D, Clock, Resetn;
output reg Q;
always@(posedge Clock)
if(!Resetn) Q<=0;
else Q<=D;
endmodule
위는 비동기 초기화, 아래는 동기 초기화이다.
동기 초기화는 Clock의 edge에서 reset신호에 따라 값이 reset되는 특징이 있다.
비동기 초기화는 한마디로 말해서 서로 다른 clock신호에 의해 동작한다. 여기서는 Reset신호의 negEdge에서 reset되고, 이때 edge condition과 if조건문의 극성이 일치하여야 한다.
결론은, 비동기식 초기화 방식에서는 reset 신호와 clk 신호가 별개로 작동한다는 것이다. 동기 초기화 방식에서는 Reset신호가 ClK에 맞추어서, Edge에 걸릴 때만 Reset 값이 바뀌게 된다.
TestBench
참고 : https://www.fpga4student.com/2017/03/verilog-code-for-counter-with-testbench.html
module upcount_top();
reg Resetn, Clock, E;
wire[3:0]Q;
upcount(Resetn, Clock, E, Q);
initial begin
clk = 0;
forever #5 clk=~clk; // what is this?
end
initial begin
reset = 1;
#20:
reset = 0;
end
endmodule
TestBench에서 input은 reg로 선언해주고, output은 wire로 설정한다.
upcount 모듈을 선언한 뒤, initia Block으로 simulation의 시작을 알린다.
simulation 시 initial block에 있는 명령들이 실행된다.
# 표시는 그 뒤에 오는 숫자만큼 시간을 지연시킨다는 뜻이다.
위의 Testbench 코드를 살짝 수정해서 시뮬레이션을 진행해보았다.
module upcount1_top();
reg Resetn, Clock, E;
wire[3:0]Q;
upcount1 mymodule(Resetn, Clock, E, Q);
initial begin
Clock = 0; Resetn = 1; E = 0;
#100; Resetn = 0;
end
always begin
#50; Clock=~Clock;
end
always begin
#150; E = 1; #50 E = 0;
#50; E = 1; #50 E = 1;
#50 E = 1; #50 E = 1;
#50 E = 1; #50 E = 1;
#50 E = 1; #50 Resetn = 1;
end
endmodule
관찰해보면, 처음에는 Reset 신호가 1인데 E 신호가 0으로 되어있으므로 Q의 값은 Unkonw이 된다.
그 다음으로 Reset = 0이이 되어서 Q = 0이 nonblocking assginment로 assign이 이루어지고,
(이때 E는 계속 1로 유지되었다. )
Clock posEdge를 만났을 때 Q <= Q + 1 assignment가 실행되어서 Q값이 1 증가한다.
추가적으로 UPcount에 조건을 더하였다.
3비트 input R과, 1비트 input L을 더 넣었다.
L = 1일 경우 Q에는 R의 값이 로드된다.
그렇지 않을 경우 보통의 down counter처럼 E = 1일때 Q값이 하나 증가된다. \
downcount
downcount도 평범하게 하지 않고,
위와 같은 조건을 넣어서 구현해보았다.
// down counter with parallel load`
module downcount(R, Resetn, Clock, E, L, Q);
parameter n = 8;
input[n-1:0] R;
input Resetn, Clock, E, L;
output reg[n-1:0] Q;
always @(negedge Resetn or posedge Clock) begin
if(!Resetn) Q <= 50;
else begin
if(L) Q <= R;
else begin
if(E) Q <= Q - 1;
end
end
end
endmodule
module upcount1_top();
reg Resetn, Clock, E;
wire[3:0]Q;
upcount1 mymodule(Resetn, Clock, E, Q);
initial begin
Clock = 0; Resetn = 1; E = 0;
#100; Resetn = 0;
end
always begin
#50; Clock=~Clock;
end
always begin
#150; E = 1; #50 E = 0;
#50; E = 1; #50 E = 1;
#50 E = 1; #50 E = 1;
#50 E = 1; #50 E = 1;
#50 E = 1; #50 Resetn = 1;
end
endmodule
upcount에서 input 비트 수를 변경시켰고, 이에 따라 random number도 살짝 다르게 나오게금 하였다.
여기서는 8비트로 하였는데, 처음에 Q에 50을 할당하였으나 그래프를 보면 32(2의 5승)부터 시작되는 것을 알 수 있다.
아 Q는 binary형식이니까 16진수로 변환해줘서 그런가?
downcount 구현할 떄 testbench에서는 일일이 값을 넣지 않고
$urandom 함수를 이용하였다.
$urandom 함수는 32비트의 정수를 출력하며, 나는 0부터 999까지의 정수가 필요했으므로
$urandom %1000을 input값으로 할당하였다.
// down counter with parallel load`
module downcount(R, Resetn, Clock, E, L, Q);
parameter n = 8;
input[n-1:0] R;
input Resetn, Clock, E, L;
output reg[n-1:0] Q;
always @(negedge Resetn or posedge Clock) begin
if(!Resetn) Q <= 8'b00001111;
else begin
if(L) Q <= R;
else begin
if(E) Q <= Q - 1;
end
end
end
//downcount_top.v
module downcount_top();
parameter n = 8;
reg [n-1:0] R;
reg Resetn, Clock,E, L;
wire[n-1:0] Q;
downcount mymodule3(R, Resetn, Clock, E, L, Q); // upcount 카운터 생성
initial begin
Clock = 0; Resetn = 1; E = 0; L = 0;
#100 Resetn = 0;
end
always begin
#50; Clock=~Clock;
end
always begin
#100; R = $urandom%1000;
end
always begin
#150; begin E = 1; L = 0; end
#50; begin E = 0; L = 0; end
#50; E = 1; L = 0; #50; E = 1; L = 1;
#50; E = 1; L = 1; #50; E = 1;L = 1;
#50; E = 1;L = 0; #50; E = 1;L = 0;
#50; E = 1; L = 0; #50; Resetn = 1;
end
endmodule
아무튼 downcount까지 구현했고, 마지막으로 updown count이다!
updown count도 다수 비트로 설계하였으며, 위의 조건이 추가되었다.
거기에다가 up_down이라는 input을 하나 더 둬서, up_down = 1이면 증가, 0이면 감소하는 형태로 구현했다.
// updowncounter
module updowncount(R, Resetn, Clock, E, up_down, L, Q);
parameter n = 8;
input [n-1:0] R;
input Clock, L, E, up_down, Resetn;
output reg[n-1:0] Q;
always@(negedge Resetn or posedge Clock) begin
if(!Resetn) Q <= 0;
else begin
if(L) Q <= R;
else begin
if(E) begin
if(up_down) Q <= Q + 1;
else Q <= Q - 1;
end
end
end
end
endmodule
//updowncount_top.v
module updowncount_top();
parameter n = 8;
reg [n-1:0] R;
reg Resetn, Clock,E, L, up_down;
wire[n-1:0] Q;
updowncount mymodule(R, Resetn, Clock, E,up_down, L, Q); // upcount 카운터 생성
initial begin
Clock = 0; Resetn = 1; E = 0; L = 0; up_down = 0;
#100 Resetn = 0;
#200 up_down = 1;
#500 up_down = 0;
end
always begin
#50; Clock=~Clock;
end
always begin
#100; R = $urandom%1000;
end
always begin
#150; begin E = 1; L = 0; end
#50; begin E = 0; L = 0; end
#50; E = 1; L = 0; #50; E = 1; L = 1;
#50; E = 1; L = 1; #50; E = 1;L = 1;
#50; E = 1;L = 0; #50; E = 1;L = 0;
#50; E = 1; L = 0; #50; Resetn = 1;
end
endmodule
이렇게 해서 마무리!!