Harman 세미콘 아카데미/Verilog

2024.6.20 [Verilog]⑤ - 비동기 카운터, 동기카운터, BCD 카운터, 업다운 카운터, 링카운터, 주종형 D플립플롭

U_Pong 2024. 6. 20. 20:22

2024.6.20 수업날

오늘은 카운터의 종류에 대해 알아보고, 주종형 D플립플롭에 대해 알아본다.


 

카운터에 대해 알아보기 전에 기본 단어에 대해 알아보자.

먼저 카운터란, 클록 펄스를 계수하는 디지털 회로이다.

카운터는 플립플롭으로 구성하며, 업카운트와 다운 카운터로 구분한다.

 

업 카운터는 계수가 계속 증가하는 카운터이다. 초기 상태를 00으로 설정하면, 클록 펄스가 주어질 대마다 증가하고, 클록 펄스가 계속 주어지면 00으로 돌아가 동작을 반복한다.

다운 카운터는 계수가 감소하는 카운터이다. 초기 상태를 11로 설정하면 클록 펄스가 계속 주어지면 11로 돌아가 계수 동작을 반복한다. 

 

 

< 비동기 카운터 >

비동기 카운터란 클록 펄스를 첫 번째 플립플롭에만 연결한다.

나머지 플립플롭들은 앞단의 출력이 클록 펄스로 작용하며, 각각 다른 타이밍으로 동작한다.

플립플롭으 동작 타이밍이 다르므로 카운터 속도가 느리지만 회로가 단순하다.

또한, 반도체로 만들어져서 커패시터 때문에 pdt(전파지연속도)가 발생하여 서로 다른 클록을 썼기 때문에 비동기 카운터라고 한다.

 

먼저 비동기 업 카운터에 대해 알아보자.

계수가 점점 증가하는 업 카운터이기 때문에 카운터의 수는 점점 증가한다.

입력값 J와 K를 하나로 묶었기 때문에 T플립플롭을 이용한 카운터라고도 볼 수 있다.

 

T플립플롭을 이용한 비동기 업카운터
//////////////////////////////////////////////////////// 2024.6.20
//////게이트만 구조적 모델링 안하는거고 인스턴트는 구조적 모델링 계속 씀
//////t플립플롭을 이용한 비동기 업카운터
module up_counter_asyc(
      input clk, reset_p,
      output [3:0] count);
      
      T_flip_flop_n T0(.clk(clk), .reset_p(reset_p), .t(1), .q(count[0]));        //t가 1이여야 토글->직접 값 입력
      T_flip_flop_n T1(.clk(count[0]), .reset_p(reset_p), .t(1), .q(count[1]));
      T_flip_flop_n T2(.clk(count[1]), .reset_p(reset_p), .t(1), .q(count[2]));
      T_flip_flop_n T3(.clk(count[2]), .reset_p(reset_p), .t(1), .q(count[3]));

endmodule

 

아래는 비동키 업 카운터의 시뮬레이션 결과이다.

초기값을 설정해 주고, 시뮬레이션을 한번 돌리고, 다시 설정을 바꿔서 시뮬레이션을 돌려야한다.

먼저 clk는 10ns로 설정해주고, reset_p는 constant에서 1, 시뮬레이션 돌리는 시간을 10ns으로 하여 시뮬레이션을 실행시킨다.

그 다음으로 reset_p를 0, 시뮬레이션 돌리는 시간을 1000ns으로 설정하여 시뮬레이션을 실행하면 1010ns에서 부터 결과값을 확인할 수 있다.

count에서 수는 계속 증가하는 업 카운터의 특징을 볼 수 있다.

 

이때, reset을 1로 설정했다가 다시 0으로 설정하는 이유는 카운터의 값을 확인해보기 위함이다.

아래 사진과 같이, reset을 처음부터 0으로 하여 시뮬레이션을 실행시키면 카운터의 값이 제대로 표시되지 않는다.

하지만, reset을 1로 설정했다가 시뮬레이션을 돌린 후, 다시 0으로 설정하여 시뮬레이션을 실행시키면 카운터의 값이 제대로 표시된다.

 

또한, 카운터의 값을 10진수로 확인하기 위해 

카운터에서 오른쪽 클릭을 하여 Radix - Unsigned Decimal을 선택하면 결과값을 10진수로 확인할 수 있다.

 

 

다음으로 비동기 다운카운터에 대해 알아보자.

계수가 점점 감소하는 다운 카운터이기 때문에 카운터의 수는 점점 감소한다.

마찬가지로 입력값 J와 K를 하나로 묶었기 때문에 T플립플롭을 이용한 카운터라고도 볼 수 있다.

 

 

T플립플롭을 이용한 비동기 다운카운터
/////////////////////////////////////////t플립플롭을 이용한 비동기 다운카운터
module down_counter_asyc(
      input clk, reset_p,
      output [3:0] count);
      
      T_flip_flop_p T0(.clk(clk), .reset_p(reset_p), .t(1), .q(count[0]));        //t가 1이여야 토글->직접 값 입력
      T_flip_flop_p T1(.clk(count[0]), .reset_p(reset_p), .t(1), .q(count[1]));
      T_flip_flop_p T2(.clk(count[1]), .reset_p(reset_p), .t(1), .q(count[2]));
      T_flip_flop_p T3(.clk(count[2]), .reset_p(reset_p), .t(1), .q(count[3]));

endmodule

 

아래는 비동기 다운 카운터의 시뮬레이션 결과이다.

비동기 업 카운터와 마찬가지로 초기 설정을 해주고, rest을 다시 0으로 바꾼 다음 시뮬레이션 돌리는 시간을 1000ns라고 설정하고 시뮬레이션을 실행하면 1010ns에서 부터 결과값을 확인할 수 있다.

count에서 수는 계속 감소하는 업 카운터의 특징을 볼 수 있다.

 

 

< 동기 카운터 >

동기 카운터는 회로의 모든 플립플롭에 클록 펄스를 동시에 인가한다.

따라서 각 플립플롭의 상태가 동시에 정해지며 동기화되어 동작하게 된다.

비동기 카운터보다 동작속도가 빠르지만, 회로가 복잡하며 단계적인 설계 과정이 필요하다.

 

먼저 동기 업 카운터에 대해 알아보자.

동기 업 카운터
///////////////////////////////////////////////////// 동기 업카운터
//베릴로그 코드는 병렬적으로 작동, 순서 상관 없다
module up_counter_p(
      input clk, reset_p, enable,
      output reg [3:0] count);      //4비트짜리 카운터
      
//      wire [3:0] inc;   //increament        
//      assign inc = count + 1;       //출력 카운터와 1 더해서 출력 내보내는 덧셈기
      
//      always @ (posedge clk or posedge reset_p) begin
//            if(reset_p) count = 0;      
//            else if (enable) count = inc;      //덧셈기의 출력을 받아 q로 내보냄    
//      end
      
      //위의 주석된 부분을 합치면 아래와 같이 간결하게 쓸 수 있다.
       always @ (posedge clk or posedge reset_p) begin
            if(reset_p) count = 0;      
            else if (enable) count = count + 1;      
      end
      
endmodule

 

 

아래는 동기 업 카운터의 시뮬레이션 결과이다.

clk는 10ns, reset_p는 constant로 0, enable은 constant로 1을 입력하고 시뮬레이션 돌리는 시간을 10ns으로 하여 한번 실행한다.

다음으로 reset_p를 1, 시뮬레이션 돌리는 시간을 1000ns으로 설정하고 실행하면 아래의 사진과 같이 나타난다.

count가 점점 증가하는 업 카운터의 특성을 볼 수 있다.

 

 

다음으로 동기 다운 카운터에 대해 알아보자.

계수가 점점 감소하는 다운 카운터이기 때문에 카운터의 수는 점점 감소한다.

동기 다운 카운터
////////////////////////////////////////////////////////// 다운 동기 카운터
module down_counter_p(
      input clk, reset_p, enable,
      output reg [3:0] count);      //4비트짜리 카운터
          
       always @ (posedge clk or posedge reset_p) begin
            if(reset_p) count = 0;      
            else if (enable) count = count - 1;       
      end
      
endmodule

 

아래는 동기 다운 카운터의 시뮬레이션 결과이다.

clk는 10ns, reset_p는 constant로 1, enable은 constant로 1을 입력하고 시뮬레이션 돌리는 시간을 10ns으로 하여 한번 실행한다.

다음으로 reset_p를 0, 시뮬레이션 돌리는 시간을 1000ns으로 설정하고 실행하면 아래의 사진과 같이 나타난다.

count가 점점 감소하는 다운 카운터의 특성을 볼 수 있다.

 

 

< BCD 카운터 >

BCD 카운터는 10진수 값을 2진수로 표현하여 0부터 9까지 순차적으로 증가하고 이 과정을 반복한다.

 

먼저 BCD 업 카운터에 대해 알아보자

BCD(10진수) 카운터
////////////////////////////////////////////////////////// 10진  업 카운터(bcd)
module bcd_up_counter_p(
      input clk, reset_p, enable,
      output reg [3:0] count);      //4비트짜리 카운터
      
       always @ (posedge clk or posedge reset_p) begin
            if(reset_p) count = 0;      
            else if (enable) begin
                  count = count + 1;      
                  if(count >= 10) count  = 0;   //10이 되자마자  0이된다 
                  // pdt같은 여러 오류 때문에 10을 놓치더라도 0으로 초기화가됨, 오동작 최소화
            end    
      end
      
endmodule

if(count == 10)이라고 하지 않고 if(count >= 10)이라고 작성한 이유는

pdt(전파지연시간)과 같은 여러 오류가 발생할 수 있기 때문에, 만약 10의 값을 놓쳐서 10보다 큰 값으로 넘어갔을때 0이 아니라 10보다 큰 값을 출력할 수 있기 때문이다.

 

 

다음으로 BCD 다운 카운터에 대해 알아보자

BCD 다운 카운터
////////////////////////////////////////////////////////// 10진 다운 카운터(bcd)
module bcd_down_counter_p(
      input clk, reset_p, enable,
      output reg [3:0] count);      //4비트짜리 카운터
      
       always @ (posedge clk or posedge reset_p) begin
            if(reset_p) count = 0;      
            else if (enable) begin
                  count = count - 1;      
                  if(count >= 10) count  = 9;   //10보다 크면  9로 초기화
            end    
      end
      
endmodule

BCD 다운 카운터는 0에서 9로 감소하는 카운터이다.

때문에 이번에도 업 카운터와 마찬가지로 if(count >= 10)이라는 형식을 사용했다.

10을 놓치고 10보다 큰 값으로 넘어갔을때, 10이상의 값을 출력할 수도 있기 때문에 10의 값만 초기화 하는 것이 아니라 10이상의 값을 9로 초기화시키는 것이다.

 

아래는 BCD 다운 카운터의 시뮬레이션 결과이다.

clk는 10ns, reset_p는 constant로 1, enable은 constant로 1을 입력하고 시뮬레이션 돌리는 시간을 10ns으로 하여 한번 실행한다.

다음으로 reset_p를 0, 시뮬레이션 돌리는 시간을 1000ns으로 설정하고 실행하면 아래의 사진과 같이 나타난다.

소스코드에서 10이상의 값을 9로 초기화 한다고 했으므로, 카운터가 0,9,8,7...1로 순차적으로 감소하는 것을 알 수 있다.

 

 

< 업다운 카운터 >

업다운 카운터란, 업 카운트와 다운 카운트를 같이 쓴 카운터로

한번에 업 카운트와 다운 카운트의 결과를 확인할 수 있다.

 

Up_Down_counter 소스코드
////////////////////////////////////////////////////////// 업다운 카운터
module up_down_counter(
      input clk, reset_p,
      input enable, up_down,
      output reg [3:0] count);
      
      //always문에는 플립플롭이 생김->레지스터
      always @(posedge clk or posedge reset_p)begin
            if(reset_p)count = 0;         //always에서 = 앞에 있는 것은 reg 변수
            else if(enable)begin
                  if(up_down)begin
                         count = count + 1;
                  end
                  else begin
                        count = count - 1;
                  end
            end
      end

endmodule

 

아래는 업다운 카운터 시뮬레이션 결과이다.

초기 설정값을 마치고, up_down 입력값을 0으로 하면 카운터가 순차적으로 줄어드는 것을 확인할 수 있다.

 

아래 사진은 up_down 입력값을 1로 했을때, 카운터가 순차적으로 늘어나는 것을 확인할 수 있다.

 

다음으로 BCD 업다운 카운터를 살펴보자

up_down_BCD_counter
////////////////////////////////////////////////////////// 업다운 bcd(10진수) 카운터
module up_down_bcd_counter(
      input clk, reset_p,
      input enable, up_down,
      output reg [3:0] count);
      
      //always문에는 플립플롭이 생김->레지스터
      always @(posedge clk or posedge reset_p)begin
            if(reset_p)count = 0;         //always에서 = 앞에 있는 것은 reg 변수
            else if(enable)begin
                  if(up_down)begin
                        if(count >= 9) count = 0;     //10 출력이 나오지 않음, 안정
                        else count = count + 1;
                  end
                  else begin
                        if(count == 0) count = 9;     //if(count == 0 || (count >= 10)) count = 9;
                        else count = count - 1;
                  end
            end
      end

endmodule

 

아래는 업다운 BCD 카운터의 시뮬레이션 결과이다.

초기 설정값을 마치고 입력값인 up_down을 0으로 하면 10진수 카운트의 값이 순차적으로 줄어들고,

up_down을 1로 하면 10진수 카운트의 값이 순차적으로 늘어나는 것을 확인할 수 있다.

 

 

< 링카운터 >

링카운터란 한개의 플립플롭만 1이되고 나머지 플립플롭은 0이되는 카운터이다.

한쪽 방향으로 순환하며 동작을 제어하는데 쓰인다.

 

링카운터
////////////////////////////////////////////////////////  링카운터
module ring_count(
      input clk, reset_p,
      output reg [3:0] q);
      
      //always 문에서 쉬프트연산을 사용할때
      always @(posedge clk or posedge reset_p) begin
            if(reset_p) q = 4'b0001;
            else begin
                  if(q == 4'b1000) q = 4'b0001; 
                  else q = {q[2:0], 1'b0};      // 하위 3비트, 최상위 1비트, 결합연산자를 가지고 쉬프트연산을 한다.
            end
      end
            
            
//      //always 문에서 case를 쓸 때
//      always @(posedge clk or posedge reset_p) begin
//            if(reset_p) q = 4'b0001;
//            else begin
//                  case(q)
//                        4'b0001 :  q = 4'b0010;
//                        4'b0010 :  q = 4'b0100;
//                        4'b0100 :  q = 4'b1000;
//                        4'b1000 :  q = 4'b0001;
//                        default   :  q = 4'b0001;
//                  endcase
//            end
//      end
      
      
      //  always문에서 if, else if를 쓸 때
//      always @ (posedge clk or posedge reset_p) begin
//            if(reset_p) q = 4'b0001;
//            else begin
//                  if(q == 4'b0001) q = 4'b0010;
//                  else if(q == 4'b0010) q = 4'b0100;
//                  else if(q == 4'b0100) q = 4'b1000;
//                  else q = 4'b0001;
//            end
//      end
endmodule

링카운터로 쓸 수 있는 구문은 총 3개가 있다.

결합 연산자를 사용한 쉬프트 연산, always문에 case문 사용, always문에 if문 사용이 있다.

어느 구문을 사용하더라도 시뮬레이션 결과는 같다.

쉬프트 연산자의 회로도는 아래와 같은데, 결합연산을 사용한 쉬프트 연산은 선만 연결하여 사용할 수 있다.

 

아래는 링카운터의 시뮬레이션 결과이다.

쉬프트 연산을 사용하여 결과를 확인한 것이고 q 값을 2진수로 보여지게 설정하면 4비트 2진수로 나타나는 것을 알 수 있다.

 

 

< 주종관계 D플립플롭 >

위의 소스코드에서는 대부분 edge 디텍터를 사용했다. edge 디텍터에서는 clk와 reset만 설정하기 때문에 다른 방법을 써야할 때도 있다.

이때 주종형 D플립플롭을 사용해야한다.

Master 플립플롭의 클롭입력은 클록펄스가 그대로 입력되고, Slave 플립플롭 부분의 클록입력에는 반전된 클록펄스가 입력되도록 구성된다.

 

주종관계 D플립플롭 하강에지 소스코드
/////////////////////////////////////////////////////
module edge_detector_n(
      input clk, reset_p,
      input cp,
      output p_edge, n_edge);
      
      reg ff_cur, ff_old;
      
      always @ (negedge clk or posedge reset_p) begin       //always문에는 순서대로 진행됨
            if(reset_p) begin
                  ff_cur <= 0;                  // always 문 안에서 논블로킹을 썼다면, 그 안의 블로킹은 모두 논블로킹으로 써야함
                  ff_old <= 0;
            end
            else begin
                       ff_old <= ff_cur;        
                       ff_cur <= cp;
                       
//                   ff_old = ff_cur;        //2비트짜리 쉬프트 레지스터, 블로킹
//                   ff_cur = cp;
                  
//                  ff_cur <= cp;                 //대입연산자, 논블로킹
//                  ff_old <= ff_cur;        
            end
      end
      
      assign p_edge = ({ff_cur, ff_old} == 2'b10) ? 1 : 0;
      assign n_edge = ({ff_cur, ff_old} == 2'b01) ? 1 : 0;


endmodule

이 소스코드에서는 Master 플립플롭을 cur(Currnet), Slave 플립플롭을 old로 설정했다.

 

clk: 10ns, reset_p: 1(constant), cp: 100ns, 시뮬레이션 돌리는 시간: 10ns로 하여 먼저 실행하고,

reset_p를 0, 시뮬레이션 돌리는 시간을 1000ns으로 하면 결과를 확인할 수 있다.

클럭 신호가 0에서 1로 전환되는 p_edge에서 cur 플립플롭이 활성화되고, 입력 cp의 값을 저장한다.

클럭 신호가 1에서 0으로 전환하는 n_edge에서 old 플립플롭이 활성화되고 cur 플립플롭의 값을 저장한다.

클럭의 p_edge에서 입력 cp 값을 cur 플립플롭에 저장하지만, old 플립플롭에는 아직 반영되지 않는다. 클럭의 n_edge에서 old 플립플롭이 cur 플립플롭의 값을 저장하므로 ff_old가 변경된다.

 

아래는 상승에지에서 작동하는 주종관계 D플립플롭의 소스코드이다.

주종관계 D플립플롭 상승에지 소스코드
/////////////////////////////////////////////////////////
//클록이 포지티브에서 동작하냐 아니면 네거티브에서 동작하냐에 따라 p, n으로 나뉨
module edge_detector_p(
      input clk, reset_p,
      input cp,
      output p_edge, n_edge);
      
      reg ff_cur, ff_old;
      
      always @ (posedge clk or posedge reset_p) begin       //always문에는 순서대로 진행됨
            if(reset_p) begin
                  ff_cur <= 0;                  // always 문 안에서 논블로킹을 썼다면, 그 안의 블로킹은 모두 논블로킹으로 써야함
                  ff_old <= 0;
            end
            else begin
                       ff_old <= ff_cur;        
                       ff_cur <= cp;
            end
      end
      
      assign p_edge = ({ff_cur, ff_old} == 2'b10) ? 1 : 0;
      assign n_edge = ({ff_cur, ff_old} == 2'b01) ? 1 : 0;


endmodule

비동기 카운터, 동기카운터, BCD 카운터, 업다운 카운터, 링카운터, 주종형 D플립플롭 끝!