PYNQで7セグ(PYNQシリーズ)

# このシリーズについて
このシリーズは，僕がPYNQ-Z2を使いこなせるようになるまでの道程を記録するものです．
知らないことや分からないことは「知らない」「分からない」と正直に書いていきますのでご教授下さると幸いです．

# ハードとソフト
- ハード
    - PYNQ-Z2
    - 7セグメントLED(型番は不明，7個のLEDにそれぞれhighを送ることで消灯，lowを送ることで点灯するタイプ)
- ソフト
    - Vivado 2022.2

# RTLモジュール
## seven_seg_driver
数字を受け取り，7セグに出力するものです．
なお，7セグはダイナミック点灯ではなく普通の点灯とし，Pmod二つで二桁表示することにします．

モジュールとしては8ビットの信号を受け取り，上位4ビットと下位4ビットをそれぞれ一桁の7セグに十六進数で表示します．
そのため，非常にややこしいですが8ビットの数字を二桁に分け，一桁につき8ビットずつ合計16ビットのレジスタで表示する形になります．
`number_2seg`関数は，4ビットの数字から7セグの信号を作っています．反転しているのは，7セグが負論理だからです．

```verilog
module seven_seg_driver(
    input [7:0] number,
    output [7:0] left_number,
    output [7:0] right_number
    );

    assign left_number = number2seg(number[7:4]);
    assign right_number = number2seg(number[3:0]);

    function [7:0] number2seg(input [3:0] number);
        case (number)
            4'h0: number2seg = ~8'b11111100;
            4'h1: number2seg = ~8'b01100000;
            4'h2: number2seg = ~8'b11011010;
            4'h3: number2seg = ~8'b11110010;
            4'h4: number2seg = ~8'b01100110;
            4'h5: number2seg = ~8'b10110110;
            4'h6: number2seg = ~8'b10111110;
            4'h7: number2seg = ~8'b11100000;
            4'h8: number2seg = ~8'b11111110;
            4'h9: number2seg = ~8'b11110110;
            4'ha: number2seg = ~8'b11101110;
            4'hb: number2seg = ~8'b00111110;
            4'hc: number2seg = ~8'b10011100;
            4'hd: number2seg = ~8'b01111010;
            4'he: number2seg = ~8'b10011110;
            4'hf: number2seg = ~8'b10001110;
        endcase
    endfunction

endmodule
```

## number_generator
7セグに表示するための数字を作成するモジュールです．
入出力にレジスタが使えることを知りませんでした．

```verilog
module number_generator(
    input clk,
    input resetn,
    
    output reg [7:0] number
    );
    
    parameter [31:0] CYCLE = 32'd50_000_000;
    
    reg [31:0] count = 32'b1;
    
    always @(posedge clk) begin
        if (!resetn) begin
            count <= 32'b1;
            number <= 8'b0;
        end else begin
            if (count == CYCLE) begin
                count <= 32'b1;
                number <= number + 8'b1;
            end else begin
                count <= count + 32'b1;
            end
        end
    end
    
endmodule
```

# ブロックデザイン
ブロックデザインは以下の手順で作成しました．
arty-s7の時はボードファイルがあると便利でしたので，PYNQ-Z2でも同様に[公式サイト](https://www.tulembedded.com/FPGA/ProductsPYNQ-Z2.html)にてボードファイルをダウンロードし，所定の位置に置いたのですが，結局ここでは使用しませんでした．

1.`Create Block Design`で空のブロックデザインを作る．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/05446b3a-e5e9-f2a4-2dbd-97e3cfb3031f.png)

2.`+`ボタンまたは`ctrl+I`を押し，`ZYNQ7 Processing System`を追加する(「zynq」などで検索するとトップに出てくる)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/1044e284-b2e9-9034-da90-fc83327b08a6.png)

3.`Run Block Automation`をクリックし，以下の様にレ点が付いていることを確認しOKを押す．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/718c7708-e266-918c-5a74-01c557da54e1.png)

4.先ほど作成した`seven_seg_driver`と`number_generator`を，ドラッグアンドドロップでブロックデザインに追加する．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/9dc25526-d7e4-6793-d55c-2af6ab6a87f2.png)

5.`Run Connection Automation`を押し，`Clock Source`を確認しOKを押す．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/235d8ffa-28ed-7448-8d57-c66d5efc05fa.png)

7.以下のように配線する(`FCLK_CLK0`と`M_AXI_GP0_ACLK`をつなぐのを忘れないように)．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/c6705fc5-44c4-ee99-058f-efc0943902a4.png)

8.`Create HDL Wrapper`し，ラッパーファイルをトップに設定する

# 制約ファイル
Pmodの部分だけですのでこれだけです．

```top.xdc
##PmodA

set_property -dict { PACKAGE_PIN Y18   IOSTANDARD LVCMOS33 } [get_ports { ja[0] }]; #IO_L17P_T2_34 Sch=ja_p[1]
set_property -dict { PACKAGE_PIN Y19   IOSTANDARD LVCMOS33 } [get_ports { ja[1] }]; #IO_L17N_T2_34 Sch=ja_n[1]
set_property -dict { PACKAGE_PIN Y16   IOSTANDARD LVCMOS33 } [get_ports { ja[2] }]; #IO_L7P_T1_34 Sch=ja_p[2]
set_property -dict { PACKAGE_PIN Y17   IOSTANDARD LVCMOS33 } [get_ports { ja[3] }]; #IO_L7N_T1_34 Sch=ja_n[2]
set_property -dict { PACKAGE_PIN U18   IOSTANDARD LVCMOS33 } [get_ports { ja[4] }]; #IO_L12P_T1_MRCC_34 Sch=ja_p[3]
set_property -dict { PACKAGE_PIN U19   IOSTANDARD LVCMOS33 } [get_ports { ja[5] }]; #IO_L12N_T1_MRCC_34 Sch=ja_n[3]
set_property -dict { PACKAGE_PIN W18   IOSTANDARD LVCMOS33 } [get_ports { ja[6] }]; #IO_L22P_T3_34 Sch=ja_p[4]
set_property -dict { PACKAGE_PIN W19   IOSTANDARD LVCMOS33 } [get_ports { ja[7] }]; #IO_L22N_T3_34 Sch=ja_n[4]

##PmodB

set_property -dict { PACKAGE_PIN W14   IOSTANDARD LVCMOS33 } [get_ports { jb[0] }]; #IO_L8P_T1_34 Sch=jb_p[1]
set_property -dict { PACKAGE_PIN Y14   IOSTANDARD LVCMOS33 } [get_ports { jb[1] }]; #IO_L8N_T1_34 Sch=jb_n[1]
set_property -dict { PACKAGE_PIN T11   IOSTANDARD LVCMOS33 } [get_ports { jb[2] }]; #IO_L1P_T0_34 Sch=jb_p[2]
set_property -dict { PACKAGE_PIN T10   IOSTANDARD LVCMOS33 } [get_ports { jb[3] }]; #IO_L1N_T0_34 Sch=jb_n[2]
set_property -dict { PACKAGE_PIN V16   IOSTANDARD LVCMOS33 } [get_ports { jb[4] }]; #IO_L18P_T2_34 Sch=jb_p[3]
set_property -dict { PACKAGE_PIN W16   IOSTANDARD LVCMOS33 } [get_ports { jb[5] }]; #IO_L18N_T2_34 Sch=jb_n[3]
set_property -dict { PACKAGE_PIN V12   IOSTANDARD LVCMOS33 } [get_ports { jb[6] }]; #IO_L4P_T0_34 Sch=jb_p[4]
set_property -dict { PACKAGE_PIN W13   IOSTANDARD LVCMOS33 } [get_ports { jb[7] }]; #IO_L4N_T0_34 Sch=jb_n[4]
```

# 結果
無事，点灯しました！

https://twitter.com/tt41357393/status/1647153142264651776?s=20

・・・のですが，しばらくするとCPUが起動してしまい，7セグは光らなくなってしまいました．

# CPUを使わずやってみる
上の原因として，ブロックデザインにzynqを含めたことが関係あるのでは？　と考察し，PYNQ-Z2に搭載されている125MHzのクロックを利用してやり直してみました．

ブロックデザインはこんな感じ．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1925706/5d3a23ba-36e3-f0dd-e395-1ded0b7ee219.png)

すると，先ほどとクロック周波数が異なるので値の更新速度こそ違いますが，途中でCPUが起動したりすることなくオーバーフローするまでカウントが進みました．

Arty S7-50しか触ってこなかったのでPYNQ-Z2のCPUの仕様がよくわからないのですが，必要ないときはブロックデザインに含めない方がいいようです．

CPUについてはまだまだ知らないことだらけな上，中身についてまで解説してくれているサイトはなかなかないので実験しながら理解していくしかなさそうです．

# 次回は
PS-PL間のデータ共有についてまとめます．