HDL 엔지니어 2021. 4. 11. 17:06

RTL(Register Transfer Level) 상에서 일반적인 Counter는 0에서부터 설계자가 정한 한계까지 클락 edge마다 1씩 증가하는 회로다.

 

예전에 합성해봤을때 아마 가산기, MUX, Flip-flop을 썼던걸로 기억한다.

 

간단한 2의 승수 카운터는 아래와 같이 구현할 수 있다.

 

module counter1(
    input clk, rst,
    output [3:0] cnt
);

reg [3:0] count;

assign cnt = count;

always @ (posedge clk or posedge rst) begin
    if (rst) begin
        count <= 4'b0;
    end
    else begin
        count <= count + 4'b1;
    end
end

endmodule

 

0~15 다음에 overflow를 이용해서 0으로 초기화하는 로직이다. 0부터 15까지 증가했다가 0으로 초기화되는 카운터다.

 

그 외의 경우 따로 초기화하는 코드를 적어줘야 한다.

 

module counter2(
    input clk, rst,
    output [3:0] cnt
);

reg [3:0] count;

assign cnt = count;

always @ (posedge clk or posedge rst) begin
    if (rst) begin
        count <= 4'b0;
    end
    else begin
        if (count == 4'd11) begin
            count <= 4'b0;
        end
        else begin
            count <= count + 4'b1;
        end
    end
end

endmodule

 

0~11까지 순환하는 카운터를 나타낸 코드다. count == 4'd11이 초기화 트리거 역할을 해낸다.

 

특정 카운터가 초기화될 때가 트리거가 되는 카운터도 있다. 코드를 nested하게 작성하면 되는데, 예시를 작성해봤다.

 

module counter3(
    input clk, rst,
    output [3:0] cnt1, cnt2
);

reg [3:0] count1, count2;

assign cnt1 = count1;
assign cnt2 = count2;

always @ (posedge clk or posedge rst) begin
    if (rst) begin
        count1 <= 4'b0;
        count2 <= 4'b0;
    end
    else begin
        if (count1 == 4'd11) begin
            count1 <= 4'b0;
            if (count2 == 4'd14) begin
                count2 <= 4'b0;
            end
            else begin
                count2 <= count2 + 4'b1;
            end
        end
        else begin
            count1 <= count1 + 4'b1;
        end
    end
end

endmodule

 

위와 같이 설계하면 count1이 11에서 0이 될때마다 count2가 1씩 증가하며, count2 또한 14까지 증가한 다음 0으로 초기화 된다.

 

이제 시뮬레이션으로 정말 말한대로 나온지 확인해보자.

 

우선 테스트벤치는 아래와 같이 작성했다.

 

`timescale 1ns/1ps

module tb_cnt();

reg clk, rst;
wire [3:0] cnt1, cnt2, cnt3_1, cnt3_2;

initial begin
    clk <= 1'b1;
    rst <= 1'b0;
    #5
    rst <= 1'b1;
    #5
    rst <= 1'b0;
    #400
    $finish;
end

counter1 TEST1(clk, rst, cnt1);
counter2 TEST2(clk, rst, cnt2);
counter3 TEST3(clk, rst, cnt3_1, cnt3_2);

always #5 clk <= ~clk;

endmodule

 

모델심으로 확인해봤다.

 

cnt1, cnt2, cnt3_1 파형
cnt3_2 파형

 

예상대로 잘 작동하는 것을 확인할 수 있다.

 

첫 파형에서 0이 유지되는 시간이 짧은 것을 확인할 수 있는데, 카운터 코드에서 rst의 posedge에서 작동하도록 작성했기 때문이다.

 

테스트벤치와 코드를 조금 수정하면 시작된 레지스터값이 조금 더 유지되게 할 수 있다.

힌트를 주자면 테스트벤치의 rst와 카운터 모듈에서의 always @ (posedge clk or posedge rst)를 수정하면 된다.

 

----

23.06.24 추가

module cnt(
    output [12-1:0] count1, 
    output [12-1:0] count2,
    input clk, rst
);

reg [6-1:0] cnt_1;
reg [6-1:0] cnt_2;

assign count1 = {cnt_2, cnt_1};
assign count2 = {cnt_1, cnt_2};

always @ (posedge clk) begin
    if (rst) begin
        cnt_1 <= 6'b0;
        cnt_2 <= 6'b0;
    end
    else begin
        if (cnt_1 == 6'h3f) begin
            cnt_1 <= 6'b0;
            if (cnt_2 == 6'h3f) begin
                cnt_2 <= 6'b0;
            end
            else begin
                cnt_2 <= cnt_2 + 6'b1;
            end
        end
        else begin
            cnt_1 <= cnt_1 + 6'b1;
        end
    end
end

endmodule

댓글1에 대한 코드입니다.

----

궁금한게 있으면 댓글 남겨주세요.