Gowin Vol.3 第1部第5章 リスト2

// 信号入力に応じてカウントアップする10進数カウンタ
module decimal_counter_with_count_up #(
parameter CLK_FREQ = 27_000_000 // 入力クロックの周波数を指定
) (
input wire clk, // 27MHzクロック入力
input wire count_up, // カウントアップ入力
output wire [3:0] counter_output[6] // 6桁の10進数カウンタの値
);

// 6桁の10進数カウントアップロジック
// count_up入力時にカウントアップする
logic [3:0] counter_reg[6];
logic [3:0] counter_next_value[6];
logic [6:0] carry;
assign carry[0] = count_up; // countup入力時、最下位のキャリーを1としてカウントアップ
// 6桁分の10進数カウンタをインスタンシエート
for (genvar i = 0; i < 6; i++) begin
// カウンタの次の値を計算する組み合わせ回路
always_comb begin
if (carry[i] == 1'b1) begin // 下の桁からのキャリーが1場合は、次の値が変わる
if (counter_reg[i] == 4'd9) begin // 現在の値が9のときは、
counter_next_value[i] = 4'd0; // - 次の値は0
carry[i + 1] = 1'b1; // - 上の桁へのキャリーは1
end else begin // 現在の値が9以外のときは、
counter_next_value[i] = counter_reg[i] + 4'd1; // - 次の値は現在の値+1
carry[i + 1] = 1'b0; // - 上の桁へのキャリーは0
end
end else begin // 下の桁からキャリーがない場合は次の値は現在の値と同じ
counter_next_value[i] = counter_reg[i];
carry[i + 1] = 1'b0;
end
end
// カウンタ値を保持するレジスタ
always_ff @ (posedge clk) begin
counter_reg[i] <= counter_next_value[i];
end
end

assign counter_output = counter_reg;

endmodule

module top (
input wire clk, // 27MHzクロック入力
input wire sw_in, // スイッチ入力
output wire [6:0] segment_output, // LED(A~G)への出力
output wire dot_output, // LED(DP)への出力
output wire [5:0] digit_output // 表示する桁の指定
);
localparam CLK_FREQ = 27_000_000;
localparam DIGIT_FREQ_HZ = 1_000;

// 使用するスイッチは負論理のため反転
wire count_up = ~sw_in;

// メタステーブル防止のため、スイッチからの入力を2段のフリップフロップで受ける
logic [1:0] count_up_reg = '0;
always @ (posedge clk) begin
count_up_reg[0] <= count_up;
count_up_reg[1] <= count_up_reg[0];
end

// スイッチ入力を2段のフリップフロップに記憶する
logic [1:0] count_up_acc_reg = '0;
always @ (posedge clk) begin
count_up_acc_reg[0] <= count_up_reg[1];
count_up_acc_reg[1] <= count_up_acc_reg[0];
end

// 2段のフリップフロップの値からスイッチ入力の立ち上がりエッジを検出する
wire count_up_posedge = ~count_up_acc_reg[1] & count_up_acc_reg[0];

// 6桁の10進数カウントアップモジュール
// スイッチが押されたときに1度だけカウントアップする
wire [3:0] decimal_number[6];
decimal_counter_with_count_up #(
.CLK_FREQ (CLK_FREQ)
) decimal_counter_with_count_up_i (
.count_up (count_up_posedge),
.counter_output (decimal_number),
.*
);

// 表示する桁を指定するモジュール
// 一定の間隔で次の桁へ移動する
wire [3:0] digit_count;
digit_driver #(
.CLK_FREQ (CLK_FREQ),
.DIGIT_FREQ_HZ (DIGIT_FREQ_HZ)
) digit_driver_i (
.*
);

// decimal_numberの値をsegment_outputをへ変換するモジュール
segment_driver segment_driver_i (
.decimal_number(decimal_number[digit_count]),
.*
);

// 4桁目の7セグにドットを表示し、小数点を表すためのロジック
assign dot_output = (digit_count == 4'd3)? 1'b1: 1'b0;

endmodule