0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PolarFire SoC 開発 #3 FPGAデザイン(HDL)

Posted at

記事概要

  • PolarFire SoC Discovery Kitの7SEG LED回路作成
  • PolarFire SoC Discovery KitのHDL作成
  • PolarFire SoC Discovery Kitの7SEG点灯実機確認

7SEG LED回路

自宅に転がっていた7セグ(実際は7セグ+小数点表示用ドット)のカソードコモンのLEDを利用します。大昔に購入したラズパイスターターキットに入っていたものです。どこのメーカの製品なのかはわかりませんが、奇跡的にデータシートだけは保存していたので、下記のようにブレッドボード上で回路を作成しました。

7セグLED ピン コネクタピン SoC外部ピン 抵抗 備考
1 IO5 W16(HSIO73PB0) 220Ω 左下(E)
2 IO4 T17(HSIO55PB0) 220Ω 下(D)
3 GND - - GND
4 IO3 Y19(HSIO67PB0) 220Ω 右下(C)
5 IO8 Y15(HSIO74PB0) 220Ω ドット(DP)
6 IO2 W20(HSIO68PB0) 220Ω 右上(B)
7 IO1 V18(HSIO57PB0) 220Ω 上(A)
8 GND - - GND
9 IO7 V15(HSIO75PB0) 220Ω 左上(F)
10 IO6 AA17(HSIO68PB0) 220Ω 中央(G)

1dig_8seg_ブレッドボード.png

7SEGLED_DATA.png

また、ラズパイで7セグLEDを試した際と同じ電圧(GPIO:3.3V)で駆動させるために、ジャンパ設定(J49)を1-2closedとして、3.3V出力としました。評価ボードでは8bit電圧変換ICで1.8Vから3.3Vに昇圧されています。

FPGAデザイン作成

HDLソースコード

1秒ごとに1ずつカウントアップする回路を作成しました。

led_7seg.sv
module led_7seg(
    input           i_clk,
    input           i_rst_n,
    output  [6:0]   o_led_7seg,
    output          o_dot
    );

    // parameter
    parameter P_W_CTR      = 28;               // width of counter
    parameter P_W_NUM      = 4;                // width of number
    parameter P_CTR_1SEC   = 28'd200000000;    // 200MHz

    // variables
    reg     [P_W_CTR-1:0]   r_ctr;
    reg     [P_W_NUM-1:0]   r_num;
    wire                    w_count_up;

    // counter
    always @(posedge i_clk or negedge i_rst_n) begin
        if (~i_rst_n)           r_ctr <= 28'h0;
        else if (w_count_up)    r_ctr <= 28'h0;
        else                    r_ctr <= r_ctr + 28'h1;
    end

    // increment number
    always @(posedge i_clk or negedge i_rst_n) begin
        if (~i_rst_n)           r_num <= 4'h0;
        else if (w_count_up)    r_num <= r_num + 4'h1;
    end

    // timing count up
    assign w_count_up = (r_ctr == (P_CTR_1SEC - 28'd1)) ? 1'b1 : 1'b0;

    // output
    //// convert number to 7 seg
    assign o_led_7seg   = f_num2seg(r_num);
    //// fix dot to 1
    assign o_dot        = 1'b1;

    // convert a number to 7 seg
    function [6:0] f_num2seg(
        input [3:0] i_num
    );
        case (i_num)
            //    LED            ABCDEFG
            4'h0: f_num2seg = 7'b1111110; // 0
            4'h1: f_num2seg = 7'b0110000; // 1
            4'h2: f_num2seg = 7'b1101101; // 2
            4'h3: f_num2seg = 7'b1111001; // 3
            4'h4: f_num2seg = 7'b0110011; // 4
            4'h5: f_num2seg = 7'b1011011; // 5
            4'h6: f_num2seg = 7'b1011111; // 6
            4'h7: f_num2seg = 7'b1110000; // 7
            4'h8: f_num2seg = 7'b1111111; // 8
            4'h9: f_num2seg = 7'b1111011; // 9
            4'hA: f_num2seg = 7'b1110111; // A
            4'hB: f_num2seg = 7'b0011111; // b
            4'hC: f_num2seg = 7'b1001110; // C
            4'hD: f_num2seg = 7'b0111101; // d
            4'hE: f_num2seg = 7'b1001111; // E
            4'hF: f_num2seg = 7'b1000111; // F
            default: f_num2seg = 7'b0000000; 
        endcase
    endfunction
endmodule

シミュレーションコード

クロックとリセット信号を駆動させて、内部信号を出力するテストベンチを作成しました。

test_led_7seg.sv
`timescale 1ns/100ps

module test_led_7seg;
    // signal for DUT
    logic       clk;
    logic       rst_n;
    logic [6:0] led_7seg;
    logic       led_dot;

    // simulate clock and reset
    initial begin
        clk     = 1'b0;
        forever begin
            #2.5    clk = ~clk;
        end
    end

    initial begin
        rst_n   = 1'b0;
        #100 rst_n = 1'b1;
    end

    // display
    initial begin
        $display("=== Seven Segment Decoder Test Start ===");

        #0
        $monitor("led_7seg.r_num = %h, led_7seg = %b, led_dot = %h at %t", dut.r_num, led_7seg, led_dot, $time);

        // stop after 31 seconds
        #15000;
        $stop;
    end

    // DUT
    led_7seg #(
        .P_CTR_1SEC (28'd100)
    ) dut (
        .i_clk      (clk),
        .i_rst_n    (rst_n),
        .o_led_7seg (led_7seg),
        .o_dot      (led_dot)
    );

endmodule

DUTは1秒ごとにカウントアップするため、普通にシミュレーションを作成すると数十秒オーダのシミュレーションになってしまいます。これではシミュレーション実行時間が長すぎるので、モジュールのパラメータを以下のように上書きして、100クロック(=500ns)でインクリメントするように変更しました。

パラメータ 変更前 変更後
P_CTR_1SEC 28'd200000000 28'd100

Libero SoCプロジェクト

#1で作成したプロジェクトをベースにして作成します。
SmartDesignで下記のようなデザインを作成しました。
smartdesign_7seg.png

HDLソースのインポート

以下2つのHDLソースをSmartDesignにインポートします。Debounce回路では、リセット入力用のスイッチがチャタリングして極短期間に複数回のリセットが繰り返されることを防止できるようです。

  • Debounce回路(デモデザインからの流用回路)
  • 7SEG LED回路(先ほど作成した回路)
  1. Design Flowタブの[Create Design > Create HDL]を右クリックして、Import Files...を選択し、インポート対象のファイルを指定する。インポートに成功すると、Design HierarchyタブのUser HDL Source Files直下にHDLが追加される。
  2. 追加されたHDLファイルをSmartDesignタブ上にドラッグ&ドロップすると、IPがインスタンスされる。
  3. 必要な接続を追加する。
  4. その後は#2と同様に、Design Check, Generate Component, Build Hierarchy, Select a rootを実施する。

シミュレーションコードのインポート

  1. Design Flowタブの[Create Design > Verify Pre-Synthesized Design > Simulate]を右クリックして、Import Files...を選択し、対象のテストベンチファイルをインポートする。
  2. stimulus HierarchyタブのBuild Hierarchをクリックする。
  3. Design Flowタブの[Create Design > Verify Pre-Synthesized Design > Simulate]を右クリックして、[Organize Input Files > Organize Simulation Files]を選択する。
  4. 表示されたウィンドウでUserを選択した後に、テスト対象のファイルをStimulus filesからAssociated Stimules filesにAddする。
  5. [Project > Project Setting]を選択し、表示されたウィンドウで[Simulation options DO file]を選択し以下のように設定を変更して、Saveして、Closeする。
項目 設定
Testbench module name テストベンチのトップモジュール名(今回はtest_led_7seg)
Simulation runtime -all

simulate_setting.png

シミュレーション実行

  1. Design Flowタブの[Create Design > Verify Pre-Synthesized Design > Simulate]を右クリックして、Runをクリックする。
  2. シミュレーションが実行され、ログが表示される。
シミュレーションログ
# === Seven Segment Decoder Test Start ===
# led_7seg.r_num = 0, led_7seg = 1111110, led_dot = 1 at                    0
# led_7seg.r_num = 1, led_7seg = 0110000, led_dot = 1 at               598000
# led_7seg.r_num = 2, led_7seg = 1101101, led_dot = 1 at              1098000
# led_7seg.r_num = 3, led_7seg = 1111001, led_dot = 1 at              1598000
# led_7seg.r_num = 4, led_7seg = 0110011, led_dot = 1 at              2098000
# led_7seg.r_num = 5, led_7seg = 1011011, led_dot = 1 at              2598000
# led_7seg.r_num = 6, led_7seg = 1011111, led_dot = 1 at              3098000
# led_7seg.r_num = 7, led_7seg = 1110000, led_dot = 1 at              3598000
# led_7seg.r_num = 8, led_7seg = 1111111, led_dot = 1 at              4098000
# led_7seg.r_num = 9, led_7seg = 1111011, led_dot = 1 at              4598000
# led_7seg.r_num = a, led_7seg = 1110111, led_dot = 1 at              5098000
# led_7seg.r_num = b, led_7seg = 0011111, led_dot = 1 at              5598000
# led_7seg.r_num = c, led_7seg = 1001110, led_dot = 1 at              6098000
# led_7seg.r_num = d, led_7seg = 0111101, led_dot = 1 at              6598000
# led_7seg.r_num = e, led_7seg = 1001111, led_dot = 1 at              7098000
# led_7seg.r_num = f, led_7seg = 1000111, led_dot = 1 at              7598000
# led_7seg.r_num = 0, led_7seg = 1111110, led_dot = 1 at              8098000
# led_7seg.r_num = 1, led_7seg = 0110000, led_dot = 1 at              8598000
# led_7seg.r_num = 2, led_7seg = 1101101, led_dot = 1 at              9098000
# led_7seg.r_num = 3, led_7seg = 1111001, led_dot = 1 at              9598000
# led_7seg.r_num = 4, led_7seg = 0110011, led_dot = 1 at             10098000
# led_7seg.r_num = 5, led_7seg = 1011011, led_dot = 1 at             10598000
# led_7seg.r_num = 6, led_7seg = 1011111, led_dot = 1 at             11098000
# led_7seg.r_num = 7, led_7seg = 1110000, led_dot = 1 at             11598000
# led_7seg.r_num = 8, led_7seg = 1111111, led_dot = 1 at             12098000
# led_7seg.r_num = 9, led_7seg = 1111011, led_dot = 1 at             12598000
# led_7seg.r_num = a, led_7seg = 1110111, led_dot = 1 at             13098000
# led_7seg.r_num = b, led_7seg = 0011111, led_dot = 1 at             13598000
# led_7seg.r_num = c, led_7seg = 1001110, led_dot = 1 at             14098000
# led_7seg.r_num = d, led_7seg = 0111101, led_dot = 1 at             14598000

タイミング制約

#2では作成しませんでしたが、今回は作成します。といっても、インスタンスしたIPで自動に設定されるタイミング制約を利用しているだけです。

  1. Design Flowタブの[Constraints > Manage Constraints]を右クリックし、Open Constraint Manager viewを選択する。
  2. 表示されたview画面でTimingタブを開く。
  3. Derive Constraintsをクリックすると、自動的に制約ファイル(top_derived_constraints.sdc)が生成される。

中身を確認してみると、クロック制約が与えられているだけのようでした。

top_derived_constraints.sdc
create_clock -name {REF_CLK_0} -period 20 [ get_ports { REF_CLK_0 } ]
create_generated_clock -name {PF_CCC_C0_0/PF_CCC_C0_0/pll_inst_0/OUT0} -multiply_by 4 -source [ get_pins { PF_CCC_C0_0/PF_CCC_C0_0/pll_inst_0/REF_CLK_0 } ] -phase 0 [ get_pins { PF_CCC_C0_0/PF_CCC_C0_0/pll_inst_0/OUT0 } ]

論理合成

#2と同様。

ピン制約

以下のようなピン制約を与えました。

ポート名 SoC外部ピン 方向
LED_DOT Y15 OUTPUT
LED_SEG[0] AA17 OUTPUT
LED_SEG[1] V15 OUTPUT
LED_SEG[2] W16 OUTPUT
LED_SEG[3] T17 OUTPUT
LED_SEG[4] Y19 OUTPUT
LED_SEG[5] W20 OUTPUT
LED_SEG[6] V18 OUTPUT
REF_CLK R18 INPUT
switch_i T19 INPUT

配置配線

#2と同様。

タイミング検証

タイミング制約を追加したので、タイミング検証も実施します。

  1. Design Flowタブの[Implement Design > Verify Post Layout Implementation > Verify Timing]を右クリックし、Runを選択する。タイミング検証が実施される。
  2. Design Flowタブの[Implement Design > Verify Post Layout Implementation > Open SmartTime]をダブルクリックする。
  3. 表示されたSmartTimeのウィンドウでSummaryを確認すると、各クロックの動作周波数を確認できる。

今回はPF_CCC_Cから出力されるクロックの動作周波数が500MHzとなっており、200MHzを下回っているので問題ないと思われます。ただ、ちょうど500MHzになっているため、正しくタイミング検証できているか疑わしいです。タイミングがシビアになりそうなデザインに取り組む際には、タイミング検証についてより詳細に確認・調査したいと思います。

書き込み

#2と同様。

実機動作確認

以下の写真のように設計通りに光ってカウントアップされました。
SW1を押すと再度0からカウントアップされました。
IMG_2374.jpg

今後したいこと

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?