신호등과 같은 CountDown 표시가 필요한 회로는 세 가지 조건을 만족해야 한다.
Condition 1.
신호등은 정해진 시간부터 0초까지 1초씩 신호가 바뀐다. 1의 자릿수가 0이 되면 그 다음에는 10의 자릿수가 1씩 감소하며, 1의 자릿수는 9가 된다. 각 자릿수의 카운터가 서로 엮여있으므로 nested counter를 쓸 것이다. nested counter에 대해서는 이전 포스트에서 언급한 적이 있어서 자세히는 설명하지 않겠다.
Condition 2.
신호등은 각자 다른 시간을 가지고 있기 때문에 그리고 언제든지 조정이 될 수 있어야하기 때문에, 값을 조정할 수도 있어야 할 것이다.
Condition 3.
또한 한번 신호가 끝나면 일정 시간 후에 다시 반복해야하기 때문에 신호가 끝나면 일정 시간 동안 기다리기도 해야 할 것이다.
카운트다운 모듈을 비롯한 제어 회로를 구현했다. 우선 카운트다운 모듈이다.
module countdown_one(
output [3:0] data_out,
input clk_in, rst, enb, EOT,
input [3:0] init_val
);
reg [3:0] data_reg;
assign data_out = data_reg;
always @ (posedge clk_in or posedge rst) begin
if (rst) begin
data_reg <= 4'b0;
end
else if (enb) begin
data_reg <= init_val;
end
else if (~EOT) begin
if (data_reg == 4'd0) begin
data_reg <= 4'd9;
end
else begin
data_reg <= data_reg - 4'b1;
end
end
end
endmodule
module countdown_ten(
output [3:0] data_out,
input [3:0] data_ones,
input clk_in, rst, enb, EOT,
input [3:0] init_val
);
// 40 to 00
reg [3:0] data_reg;
assign data_out = data_reg;
always @ (posedge clk_in or posedge rst) begin
if (rst) begin
data_reg <= 4'b0;
end
else if (enb) begin
data_reg <= init_val;
end
else if (~EOT) begin
if (data_ones == 4'd0) begin
if (data_reg == 4'd0) begin
data_reg <= init_val;
end
else begin
data_reg <= data_reg - 4'b1;
end
end
end
end
endmodule
one과 ten을 나눈 이유는 다른 용도에서도 사용할 수 있게끔 하기 위해서다. 예를 들어 분/초 카운터가 있으면 초 단위의 data_out을 이용해서 분 단위 카운터를 제어할 수 있을 것이다.
다음 코드에서 다루겠지만 이 두 모듈들은 연결되어 있는 카운터라고 생각해도 되는데, 이유는 ten의 input 중 data_ones가 one의 data_out과 연결되기 때문이다. 위 두 모듈을 이용해 Conditino 1을 만족시켰다.
enb가 1인 경우에는 레지스터 값을 init_val로 무조건 바꾸게 되는데, Condition 2를 만족하기 위함이다.
EOT는 다음 코드에서도 확인할 수 있지만 End Of Traffic을 줄인 말이다. 그냥 변수 이름 길게 쓰기 싫어서 줄였다. EOT가 0일 경우에만 작동한다는 것은 EOT가 1, 즉 신호가 끝났을 때는 동작하지 않는다는 것이다. 이를 통해 Condition 3의 일부를 만족시켰다(멈추는 기능은 있지만 기다리는 기능은 다른 모듈에 있다).
이제 두 모듈을 연결하는 모듈을 살펴보자.
module time_traffic(
output [7:0] data_traffic_out,
output End_of_Traffic,
input clk, rst, enb,
input [7:0] init_val
);
wire clk_ten;
wire [7:0] data_count;
wire enb_countdown = enb;
assign data_traffic_out = data_count;
assign End_of_Traffic = (data_count == 8'b0) ? 1'b1 : 1'b0;
countdown_one ones(
.data_out(data_count[0 +: 4]),
.clk_in(clk),
.rst(rst),
.enb(enb_countdown),
.init_val(init_val[0 +: 4]),
.EOT(End_of_Traffic)
);
countdown_ten tens(
.data_out(data_count[4 +: 4]),
.clk_in(clk),
.rst(rst),
.enb(enb_countdown),
.init_val(init_val[4 +: 4]),
.data_ones(data_count[0 +: 4]),
.EOT(End_of_Traffic)
);
endmodule
End_of_Traffic 변수는 8비트 변수인 data_count, 카운터에 표시되는 숫자들이 전부 0일 경우에만 1이 된다. 이를 통해 신호가 끝난 시점을 확인할 수 있다.
module top(
output [7:0] data_out,
input clk, rst,
input [7:0] init_val
);
reg enb;
reg [3:0] countenb;
wire End_of_Traffic;
time_traffic TRAFFIC(
.clk(clk),
.rst(rst),
.enb(enb),
.init_val(init_val),
.data_traffic_out(data_out),
.End_of_Traffic(End_of_Traffic)
);
always @ (posedge clk) begin
if (rst) begin
enb <= 1'b0;
countenb <= 4'b0;
end
else begin
if (End_of_Traffic) begin
if (countenb == 4'd11) begin
countenb <= 4'b0;
enb <= 1'b0;
end
else if (countenb == 4'd10) begin
enb <= 1'b1;
countenb <= countenb + 4'b1;
end
else begin
enb <= 1'b0;
countenb <= countenb + 4'b1;
end
end
end
end
endmodule
마지막 모듈인 top 모듈이다. top 레벨에서 Condition 3의 기다리는 기능을 구현했다. 위의 코드에서는 12 클락 동안 기다리게 된다.
이제 제대로 동작하는지 살펴보자. 테스트벤치는 아래와 같다.
`timescale 1ns/1ps
module tb_traffic();
reg clk, rst;
reg [7:0] init_val;
wire [7:0] data_out, data_out_top;
wire End_of_Traffic;
top TEST(
.clk(clk),
.rst(rst),
.init_val(init_val),
.data_out(data_out_top)
);
initial begin
clk <= 1;
rst <= 0;
#10
rst <= 1;
#10
rst <= 0;
end
initial begin
init_val <= 8'h42;
#500
init_val <= 8'h30;
#500
init_val <= 8'h22;
end
always #5 clk <= ~clk;
endmodule
시작은 42초이며 30초 및 22초로 값이 재설정된다.
파형은 다음과 같이 나타나게 된다.
'Verilog HDL 설계' 카테고리의 다른 글
Simple Finite State Machine implementation (0) | 2021.04.29 |
---|---|
테스트벤치 작성 예시 (0) | 2021.04.25 |
BCD to 7 segment (0) | 2021.04.19 |
간단한 Deinterleaver (0) | 2021.04.18 |
D latch, Master-Slave D flip-flop 구현 (gate level) (0) | 2021.04.17 |