モノ創りで国造りを

ハード/ソフト問わず知見をまとめてます

Intel FPGAのSimulationの手順

Intel FPGAのSimulation

FPGAの設計は時間がかかる。
特にデバッグに時間がかかるので、コードの誤りは早い段階で検知しておきたい。
そのためにはSimulationを使いましょう。
ここではIntel FPGAのModelsim-Alteraの使い方を簡単にまとめる。

流れ

  1. プロジェクトの作成
  2. verilog fileの作成
  3. テストベンチのテンプレートの作成
  4. テストベンチの作成
  5. Modelsimの起動とSimulationの実行

プロジェクトの作成

Quartus PrimeのFile > New Project Wizardをクリック
適当に進めてProjectを作成する
この画面でModelsim-alteraを選択する f:id:yuji2yuji:20190809105131p:plain

verilog fileの作成

  1. Quartus PrimeのFile > Newをクリック
  2. Design Files > Verilog HDL Fileをクリック
    f:id:yuji2yuji:20190809104738p:plain

  3. Verilogで書く。

  4. Start Analysys & Synthesisをクリックし、エラーが出ないことを確認する。
    f:id:yuji2yuji:20190809104944p:plain

テストベンチのテンプレート作成

  1. Quartus PrimeのProcessing > Start > Start Test Bench Template Writerをクリックする。
    f:id:yuji2yuji:20190809105600p:plain

  2. プロジェクトフォルダ/simulation/modelsimフォルダ内に、"モジュール名".vtのファイルが生成される。
    これがテストベンチのテンプレート。
    入出力と内部信号が記載されている。便利。

テストベンチの作成

  1. 生成されたテストベンチテンプレートファイルを編集する。
    ご丁寧にinitialとalwaysまで記載されているので、そこにinput信号の記載。

  2. プロジェクトを右クリックしてSettinsを選択。
    f:id:yuji2yuji:20190809111218p:plain

  3. EDA Tool SettingsのSimulationを選択。
    f:id:yuji2yuji:20190809111121p:plain

  4. NativeLink settingsのCompile test benchをチェックし。Test Benchesをクリック。
    f:id:yuji2yuji:20190809111102p:plain

  5. Newをクリック。
    f:id:yuji2yuji:20190809111615p:plain

  6. テストベンチ名を適当に入力し、Test bench and simulation filesのFile nameに作成したテストベンチを選択して入力してOKをクリック。
    f:id:yuji2yuji:20190809111025p:plain

  7. 再びプロジェクトを右クリックしてSettingsを選択し、以下の画面のFilesをクリック。File名に先ほど作成したテストベンチを選択して追加する。
    f:id:yuji2yuji:20190809112048p:plain

Modelsimの起動とSimulationの実行

  1. ModelSimのPathを設定するため、Quartsu Primeの Tools > Optionをクリック。
    f:id:yuji2yuji:20190809115057p:plain

  2. GeneralのEDA Tool OptionsのMdelSim-AlteraにPathを選択して記入する。
    自分の場合は"C:\intelFPGA_lite\17.1\modelsim_ase\win32aloem\" f:id:yuji2yuji:20190809114958p:plain

  3. Modelsim-alteraを起動するため、Quartus Primeの Tools > Run Simulation Tool > RTL Simulationをクリック。 f:id:yuji2yuji:20190809111855p:plain

  4. workの中にテストベンチがある事を確認する。
    f:id:yuji2yuji:20190809113615p:plain

  5. 記述に誤りがありコンパイルができず表示されない。
    その場合はテストベンチを修正し、Compile > Complieをクリックし、テストベンチをコンパイルする。
    f:id:yuji2yuji:20190809113532p:plain

  6. Simulationを実行するため作成したテストベンチのファイル名をダブルクリックする。
    Waveタブをクリックし波形を表示する。
    f:id:yuji2yuji:20190809114003p:plain

  7. 閲覧したいネットをドラッグしWaveにドロップする(まだ波形は表示されない)。
    f:id:yuji2yuji:20190809114132p:plain

  8. 閲覧したい波形の最大時間を設定し、Runボタンをクリックする。
    f:id:yuji2yuji:20190809114449p:plain

  9. 波形が表示される。
    f:id:yuji2yuji:20190809114514p:plain

まとめ

手順が多いので地味に大変。基本は以下の3つ。

  1. テストベンチファイルを記述
  2. テストベンチの作成
  3. プロジェクトにテストベンチファイルを追加

2と3のいずれかをうっかり忘れてしまいがちなので
上手くいかない場合は、2か3をやり忘れていないか要確認。

I2C通信で温度を測定する

デジタル測温IC

温度を測定するICの種類は大きく2つ。
アナログ出力とデジタル出力。

アナログ出力タイプの測温IC

アナログ出力タイプのICは、温度に応じた電圧の信号を出力する。
例えばアナログデバイセズのTMP3xシリーズだと
f:id:yuji2yuji:20190807150541p:plain
ICに電源供給するだけで信号が出力されるので、ICの使い方自体は極めて簡単。
しかし、アナログ信号なので、システムに用いる場合はADCで受けてデジタル変換する必要がある。

デジタル出力タイプの測温IC

デジタル出力タイプのICは温度に応じたデジタル信号を出力する。
IFとしてI2C、SPIが一般的。
今回はI2CのMCP9808を使用。
MCP9808のデジタル信号出力は以下。
f:id:yuji2yuji:20190807150800p:plain
データとして2バイト出力される。
デジタル出力タイプのICは、電源供給するだけでデジタル信号は出力されない。
デジタル出力タイプのICを使用するには、
IFのプロトコル(今回はI2C)とICそのものについて
多少の理解が必要。

MCP9808の使い方

I2Cはマスターとスレーブがある。
スレーブは複数ある。
スレーブはそれぞれが異なるアドレスを有する。
マスターが全スレーブに対してアドレス付きのデータ送信し、
指定されたアドレスのスレーブがデータを返送することで
データの送受信が行われる。

MCP9808は常にSlaveとして扱われる。
MCP9808は3bitのSlaveアドレス用端子がある。
Slaveアドレスを8つから選択できる。
f:id:yuji2yuji:20190807151424p:plain

マスターがMCP9808へアドレスを書き込む場合は、末尾を0としたデータを送信する。
例えば、アドレスを指定する場合は、以下のデータを送信する。

A6 A5 A4 A3 A2 A1 A0 R/W
0 0 1 1 x x x 0

MCP9808は温度データを出力するだけでなく
アラート信号を出力する温度の閾値や、
出力する温度の分解能なども読み書きできる。
f:id:yuji2yuji:20190807152441p:plain

MCP9808との通信

MCP9808から温度データを取得する場合の通信について述べる。 MCP9808のアドレスは0x18とする(A2:0、A1:0、A0:0)
全体の流れは

  1. マスターがスレーブにアドレスを送信する
  2. マスターがスレーブに読み出したいデータを指定する
  3. スレーブがマスターにデータを返送する
手順

1.マスターからスレーブに0x18の末尾に0を付加したデータを送信する。

A6 A5 A4 A3 A2 A1 A0 R/W
0 0 1 1 0 0 0 0

2.温度情報を読み出すため、マスターからスレーブへ0x05を送信する。
末尾に0を付加する必要はない。

3.スレーブからデータが2バイト返送される。

SoC FPGAでLチカ --DE10-Nanoを例に--

SoC FPGAとは

SoCFPGAとは、FPGAのチップにCPUの類のものが搭載されているもの。

やったこと

今回行ったのは以下

  1. FPGAのハードウェアロジックでLEDを点滅
  2. LinuxFPGAボードのLEDを点滅

今回使用したボード

DE10-Nano(IntelのCyclone Vが実装されたもの)
Digikeyで15,000円くらい。カバーがついてて心なしか安心。
書き込みが専用ツールでなく、USBケーブルで出来るのもうれしい。 f:id:yuji2yuji:20190730161014p:plain

ハードウェアロジックでLEDを点滅

Quartus Primeを立ち上げて新規プロジェクトを作成する。
ボードは選択しないで、FPGA"5CSEBA6U23I7DK"を選択。

Verilogコードの作成

Verilogのコードは以下。
クロックが50MHzなので、LEDの点滅が目で追えないけど、
スイッチ押せば止まるので気にしない。

module LED_TEST(
    input I_CLK_50MHZ,
    input I_SW1,
    output reg O_LED1,
    output reg O_LED2
    );

    reg [15:0] CLKCNT;
    reg [15:0] HALFCNT;
        
    initial begin
        O_LED1 = 1'b0;
        O_LED2 = 1'b1;
        CLKCNT = 16'b0000_0000_0000_0000;
        HALFCNT = 16'b1000_0000_0000_0000;
    end
    
    always@( posedge I_CLK_50MHZ ) begin
        CLKCNT = (I_SW1)? CLKCNT + 1:CLKCNT;
        O_LED1 = (CLKCNT < HALFCNT)? 1:0; 
        O_LED2 = ~O_LED1;
    end
endmodule

ピンアサインの設定

次にVerilogコードのInput、OutputとFPGAのピンを紐づける。
DE10-NanoのUser Manualに記載がある。
とりあえず必要な箇所だけピックアップして以下に示す。
f:id:yuji2yuji:20190730161821p:plain
f:id:yuji2yuji:20190730161908p:plain
f:id:yuji2yuji:20190730161928p:plain

Quattus PrimeのAssignments > Pin Plannerをクリックし、VelirogのInput、outputと各ピンを接続する。
今回は以下のような感じ。
f:id:yuji2yuji:20190730162313p:plain Node NameはVerilogに記載した名称を入力し、LocationにFPGAのピンアサインを入力する。
I/O Standardの箇所で電圧レベルや種類(シングル or 差動等)を選択する。
Directionはコンパイル時に自動で設定される。

コンパイル

Quartus PrimeのProcessing > Start Compilationをクリックし数分待てば書き込みデータが完成。

書き込み

Quartus PrimeのTools > Programmerをクリックすると、Programmerが起動する。
Auto Detectをクリックすると、基板上のメモリとFPGAが表示される。
FPGA上で右クリックし、edit > change fileを選択。
先ほど作成したコンパイルデータを選択する(projectディレクトリ直下のoutput_files内にあるはず)。
Program/Configureにチェックを入れて、Startをクリック。
無事書き込まれると、右上に100%(Successfuss)が表示される。

動作確認

LED0とLED1が点灯している(実際には高速で点滅している)。 Key0スイッチを押すと、一方のみが点灯する。

以上で、ハードウェアロジックでLEDを操作する説明終了。

LinuxからLEDを操作

次はボード上のLinuxからLEDを操作する。

準備

準備として、以下のDE10-Nanoの演習を一通りこなす。
service.macnica.co.jp

上記の5.演習 3は飛ばしてもよい。
ベアメタルアプリケーション開発はライセンス費用が掛かるので、
よほどのプロでない限りは使用しないはず。

Linuxアプリケーションの開発方法

6.演習4でLinuxアプリケーションの演習があるが、
Hello worldを表示できれば、
開発環境が構築されたことを意味する。
ただしHello Worldだけでは物足りないので、
LEDを点灯させてみる。
演習を完了している場合、LED PIOのベースアドレスは0x0001_0040となっている。
実際にはブリッジのベースアドレスが0xFF20_0000なので、
0xFF21_0040である(演習のpdfに記載されている)。
0xFF21_0040の各bitが各LEDの値に対応している。

レジスタの値を変えるにはmmapを使用する。

#include        <stdio.h>
#include        <stdlib.h>
#include        <fcntl.h>
#include        <string.h>
#include        <time.h>
#include        <sys/time.h>
#include        <poll.h>
#include        <sys/types.h>
#include        <sys/mman.h>
#include       <sys/stat.h>
#include       <unistd.h>
#include       <stdint.h>
#include       <errno.h>

#define    BASE_BRIGDE 0xff200000
#define    BASE_LEDPIO 0xff210040

#define DEVNAME "/dev/mem"

int main(int argc, char** argv) {
    int fd;
    uint32_t from;
    uint32_t num;
    uint32_t port;
    volatile uint8_t *iomap;
    printf("this is IOmemory test program\n");

    fd = open(DEVNAME, (O_RDWR|O_SYNC));
    if(fd <= 0){
        perror(DEVNAME);
        exit(1);
    }

    from = BASE_BRIGDE;
    num = 0x20000;
    iomap = mmap(0, num, PROT_READ|PROT_WRITE, MAP_SHARED, fd, from);
    if (iomap == MAP_FAILED) {
        printf("Error : mmap failed. ERRO:%d\n", errno);
        exit(1);
    }

        //LEDを1つだけ点灯させる。プログラム。
    port = BASE_LEDPIO;
        *(uint32_t*)(iomap + port - from) = ~0x01;
    

        munmap((void*)iomap, num);

    close(fd);
    return 0;
}

LEDを右から左、左から右へと順番に消灯したい場合は以下のようなプログラムになる。

        port = BASE_LEDPIO;
    uint8_t ledMask = 0x01;
    uint8_t ledDir = 0;
    int loopCount = 0;
    while(loopCount < 60){
        *(uint32_t*)(iomap + port - from) = ~ledMask;
        usleep(100*1000);
        if(ledDir == 0){
            ledMask <<= 1;
            if(ledMask == (0x01 << 6)){
                ledDir = 1;
            }
        }else{
            ledMask >>= 1;
            if(ledMask == 0x01){
                ledDir = 0;
                loopCount++;
            }
        }
    }

RN4870のIOピンの使い方がよくわかんねーっす

RN4870のデータシートにはこんな記載がある。

f:id:yuji2yuji:20190723000041p:plain
上から三つ目、P0_2の機能としてAD2との記載がある。

一方、User's guideを見ると
f:id:yuji2yuji:20190723000202p:plain
ADコンバータの機能を有するピンにP0_2の記載がない・・・

どういうこと??

とりあえずMicrochipのForumで聞いてみるもなかなか返答がない。
自分の英語がクソすぎるからか?

技術士試験 2019 を受けてきた!

ついにやってきた技術士試験

いやー、もーね、ほんとね、4月に願書出して、数ヶ月間
全く持って勉強しなかったわけですよ。
でも流石にノー勉はまずいと思って、試験前日に一夜漬けで勉強したわけですよ。
いろんな技術を深掘りして、ノートにあれこれまとめて。
でもね。

いざ蓋を開けてみたら、勉強する必要ほぼなかったなと。
例えば、1問目は人口減少に関する将来の課題と技術的な対策方法について小論文を書くわけですが
これ技術知らなくても書けるだろと。
新聞読んでるような人は誰でも書けるんじゃないかと。

で、2問目が技術に関するものだったわけだが、
4つのテーマから一つ選べばいいんだけど、
自分の知ってる技術領域が一個でもあればなんとかなるわけで。
幸い何個か知ってる技術領域の問題があったら難なくクリアしたわけです。

3問目はこれまたテーマの選択なんですが、今度は2つしかテーマがないわけです。 選択するテーマは、可視光通信RFIDについてでした。
でもその分野について十分に知識がないと書けないかというと、そういう問題でもなく、
仕事の進め方とか、判断方法とか、実務に関する内容を記述させる問題で、
実際私はRFIDのプロでもなんでもないんですが、
わずかに読みかじった程度の知識で文章を書きまくりました。
ざっくりでも知識がある分野の問題ならなんとかなる感じでした。

最後も2つのテーマから選択する方式でしたが、
これまたざっくりとした設問で、特定の領域を深く把握していなくても書けるような問題でした。
テーマはユニバーサルデザインとスマート農業。
これも新聞読んでりゃ書けるだろと。

ただ、これだけ生意気を言ってますが
多分受かってないと思います。
というのは、小論文を書くことに慣れておらず、
時間配分も謝ったので、後半になるに連れて文章が雑。
あと字がめちゃくちゃ汚い・・・

受かってもない私が言うのもおこがましいですが、
技術士の勉強をされる方は、技術の勉強よりも、
小論文の書き方を勉強した方が良いです。
あと新聞を読みましょう。

BLEモジュールRN4870/71の使い方まとめ

BLE通信モジュール RN4870/71

RN4870/71はMicrochip社製のBLEモジュール。
f:id:yuji2yuji:20190712105309p:plainf:id:yuji2yuji:20190712105327p:plain
技適取得済のモジュールなので、機器に埋め込んで使用できる。
RN4870/71はGPIO、ADC、I2C、UART、SPIが使用可能。
加えてスクリプトモードでスタンドアローンに動作できる。
スクリプトモードは、事前に動作させたい内容をBLEモジュールに記録することで、
例えば電源ON後や通信接続後などに実行したい操作を自動で行わせられる。
この機能を使用することで、簡単な操作であれば、Hostコントローラーが不要になる。 ブロック図は以下。
f:id:yuji2yuji:20190712105441p:plain

モジュールによって使用可能な機能とピン数が異なる。以下の通り。

機能 RN4870 RN4871
GPIO 4 2
ADC 5 2
I2C 1 1
UART 1 1
SPI 1 0

*GPIO、ADC、I2Cのピンは兼用。

RN4870は多機能だがサイズが大きい。RN4871はサイズは小さいが機能が少ない。
目的に応じて選びましょう。

RN4870 RN4871
サイズmm 12 x 22 9 x 11.5

とにかく簡単に使う

Bluetoothは生活になじみ深いので、使い方も把握しているつもりだったが、
いざモジュールを使うとなると、
GAP、GATT、Advertisement、UUIDなどなど
前提知識が色々必要でちんぷんかんぷん。

各用語は他人様の記事で勉強するとして、
ここではデフォルト状態の2つのRN4871モジュールを通信させるための
必要最小限の手順を書く。

実現したいこと:
CentralモジュールをPCで操作して、PeripheralモジュールのAD値を取得する。
* 通信モジュールはCentralとPeripheralに分類される。
* いわゆるMasterとSlave、HostとClientの関係。

手順の概要

必要な手順は以下の通り。

  1. PeripheralのMACアドレスを把握する
  2. Peripheralが自分の存在を周囲に発信する(Advertisement)
  3. Centralが特定のMACアドレスを持つPeripheralをスキャンする
  4. 接続後、Centralをリモートコマンドモードに変更
  5. PeripheralのAD値を取得する

PeripheralのMACアドレスを把握する

  • PeripheralとPCをUART接続する。
  • コマンドモードへ移行するため $$$と入力する。
  • ECHOをONにするため+と入力する(やらなくてもよい)。
  • 詳細情報を取得するため、Dと入力する。
  • BTA=xxxxxxxxと表示されるので、このxxxxxxxxをメモする。

Peripheralが自分の存在を周囲に発信する(Advertisement)

  • 電源を接続すると自動で実行される。
  • Interval時間や送信強度などを変更することで電力を抑制できるが、
  • ここでは割愛。

Centralが特定のMACアドレスを持つPeripheralをスキャンする

  • CentralとPCをUART接続する。
  • $$$と入力しコマンドモードに移行する。
  • +でECHOをON。
  • C,0,xxxxxxxxと、先ほどメモしたMACアドレスを入力し特定のモジュールをスキャンする。
  • 接続されたら再び$$$でコマンドモードにする。
  • Bと入力し接続をSecureにする。

接続後、Centralをリモートコマンドモードに変更

  • SS,C0を入力し、UART Transparentモードに移行する。
  • !,1と入力し、リモートコマンドモードへ移行。これでCentralへのコマンドが全てPeripheralへ送信されるようになる。

PeripheralのAD値を取得する

  • @,4でPeripheralモジュールの消費電流のADC値を取得する。
  • @,5でPeripheralモジュールの温度センサのADC値を取得する。

UpBoardの各IFの使用方法

UARTの使い方

UpBoardのピンアサインは以下の通り。
f:id:yuji2yuji:20190625131019p:plain
コネクタのPin8がUARTの送信、Pin10がUartの受信に使用するピンです。

UARTの受信

UpBoardでUARTを受信できるか確認するため、ArduinoからUARTを送信しUpBoardで受信してみます。
ボーレート以下で設定します。

$ stty -F /dev/ttyS1 115200

*ピンマップにはttyS4と記載されていますが、それはUbuntuなどの場合
*ubilinuxだとttyS1が正しいようです
UARTで受信したデータは以下に保存されます。

$ /dev/ttyS1

データをリアルタイムで確認するならcatコマンドを使用します。

$ cat /dev/ttyS1

Arduinoのプログラムは以下です。

void setup() {
  Serial.begin(115200);
}

void loop() {
  Serial.print("Hello world\n");
}

UARTの送信

次はUpBoardからArduinoにUART送信してみます。

$ echo "Hello world" > /dev/ttyS1

と打てば、Arduinoのシリアルモニターに文字が出力されます。
Arduinoのプログラムは以下。

void setup() {
  Serial.begin(115200);
}

void loop() {
  char inputchar;
  inputchar = Serial.read();
  if(inputchar != -1 ){
    Serial.print(inputchar);
    delay(200);
}

UpBoardとArduinoを接続し、上述のcatコマンドを実行することで、
ターミナル上にHello Worldが出続けます。

I2Cの使い方

UpBoardでI2Cが使えるようにするため、以下のコマンドを実行しましょう。

$ sudo apt install i2c-tools

UpBoardのI2Cは、i2c-0とi2c-5の2つあります。
i2c-5に接続されるデバイスのアドレスを検出するには、以下のコマンドを実行します。
sudoでないとコマンドが見つからないので注意。

$ sudo i2cdetect -r -y 5

I2Cデバイスに以下のデータを送りたい場合、
 送信するデータ:0xFF, 0xF0
 レジスタのオフセット: 0x40
 アドレス:0x60
実行するコマンドは次の通り。

$ sudo i2cset -y 5 0x60 0x40 0xFF 0xF0 i
  • Arduinoと接続して動作確認を試みましたが、まだうまくいってません・・・