Verilog-HDL SystemVerilogの作法勉強
ステッピングモーターを制御するsvコードを例にhdlの作法を勉強
Interface誌2022年12月号別冊付録「2500円ボードで始めるFPGA開発」の応用編
その2のコードを例にverilogのお作法を勉強。備忘録。
default nettypeでエラー防止
`default_nettype none
default_nettype none:
コード内で明示的にデータ型(wire や logic など)が宣言されていないネット(信号線)が存在
する場合に、エラーを発生させるようにするディレクティブ。
これにより、意図しない暗黙的なネットの生成を防ぎ、設計の意図を明確にすることができる。
他にも「default_nettype wire:」という宣言でm、明示的にデータ型が宣言されていないネットを
デフォルトでwire 型として扱うようにするディレクティブを比較的使う。
モジュールと#を使ったインスタンス化
module top #( // Millisecond per step parameter MS_PER_STEP = 100 ) ( input wire clk, output logic phase_a, output logic phase_b ); // Timer logic overflow; timer #(MS_PER_STEP * 27000) timer_i (.*);
「#」はモジュールのパラメータ値を上書き(またはインスタンス化)するために使用される記号。
topの例では、モジュールのインスタンス化とパラメータの上書きを行っている。
top モジュール内で timer モジュールを timer_instance という名前でインスタンス化している。
timer #(MS_PER_STEP * 27000) の部分がパラメータの上書きを行っているところ。
「#(...)」 の括弧内には、上書きしたいパラメータの値を記述する。この例では、timer モジュールの
COUNT_MAX パラメータに対し、top モジュールで定義された (MS_PER_STEP と*27000)に再設定
パラメータ名は省略、定義順に値を指定できるが、可読性の観点から名前を指定する方法 が推奨。
//元のコードの省略したインスタンス化部分 timer #(MS_PER_STEP * 27000) timer_i ( .clk(clk), .overflow(overflow) ); //推奨されるパラメータ名を明示的に指定した記述 timer #( .COUNT_MAX(MS_PER_STEP * 27000) ) timer_i ( .clk(clk), .overflow(overflow) );
「(.*)」はポート接続の簡略記法で、timer モジュールと top モジュールの間で、同じ名前のポートを
自動的に接続。この場合、clk と overflow ポートが接続される。
//簡略化記法 timer #(MS_PER_STEP * 27000) timer_i (.*); // 簡略化していない記法 timer #(MS_PER_STEP * 27000) timer_i (.clk(clk), .overflow(overflow));
timer #(MS_PER_STEP * 27000) timer_i ( は、timer モジュールを timer_i という名前でインスタンス化
し、その際に timer モジュールで定義されている COUNT_MAX 値を MS_PER_STEP * 27000 に再設定
(オーバーライド)
これにより、同じ timer モジュールを異なるパラメータ値で複数回インスタンス化し、それぞれ異
なる動作が可能。これは、再利用性の高い設計を実現するための重要な機能の一つ!
「#」はParameter Override Association List、またはParameter Override と呼ばれる。
文脈によっては、単に「ハッシュ記号」や「シャープ記号」と呼ばれることもある
パラメータ・オーバーライド。なんかかっこいい。
always_ff(順序回路)
// Phase counter logic [1:0] phase_counter = 'd0; always_ff @ (posedge clk) begin if (overflow) begin phase_counter <= phase_counter + 'd1; end end
always_ff @ (posedge clk) begin ... end: (posedge clk) で動作する順序回路(フリップフロップ)
if (overflow) begin ... end: タイマーモジュールからの overflow 信号がHighになったとき、
つまり指定されたステップ間隔が経過したときに、内部の処理を実行
「<= 」はノンブロッキング代入と呼ばれ、クロックの立ち上がりエッジですべての
ノンブロッキング代入が同時に実行されることを意味する。
順序回路は基本的にノンブロッキング代入を使う
always_comb(組み合わせ)回路
// Phase output always_comb begin case (phase_counter) 0: begin phase_a = 'b1; phase_b = 'b1; end 1: begin phase_a = 'b0; phase_b = 'b1; end 2: begin phase_a = 'b0; phase_b = 'b0; end 3: begin phase_a = 'b1; phase_b = 'b0; end endcase end endmodule
always_comb begin ... end: 入力信号の変化に即座に応答する組み合わせ回路を定義。
always_comb ブロック内のステートメントは、そのブロック内で参照されている入力信号
の値が変化すると、即座に再評価され、出力信号が更新されるべきであることを示す。
これは、現実世界の組み合わせ回路の動作(入力が変われば遅延なく出力も変わる)
をモデル化している。以下の特徴がある。
■イベント駆動ではない
always_ff のように特定のクロックエッジなどのイベントに
同期して動作するのではなく、入力信号の変化に敏感に反応。
■非ブロッキング代入 (<=) は推奨されない:
always_comb ブロック内では、通常ブロッキング代入 (=) を使用。これは、組み合わせ論理
では信号の順序が重要ではなく、右辺の最新の値が即座に左辺に反映されるべきであるため。
非ブロッキング代入 (<=) は、クロック同期された順序回路 (always_ff) で、同じクロックエッジ
での複数の代入を同時に行うために使用される。always_comb で非ブロッキング代入を使用すると
意図しない遅延や競合状態が発生する可能性がある。
■自動的な感性リスト(sensitivity list)
SystemVerilog では、always_comb ブロックは自動的な感性リストを持つ。つまりブロック内で
読み取られているすべての入力信号が、暗黙的に感性リストに追加される。
これにより、感性リストの記述ミスによるシミュレーションと合成の不一致を防ぐことができる。
※Verilog HDL (Verilog-2001以降) でも同様の機能を持つシミュレータが多い
$clog2() システム関数
module timer #( parameter COUNT_MAX = 27000000 ) ( input wire clk, output logic overflow ); logic [$clog2(COUNT_MAX+1)-1:0] counter = 'd0; always_ff @ (posedge clk) begin if (counter == COUNT_MAX) begin counter <= 'd0; overflow <= 'd1; end else begin counter <= counter + 'd1; overflow <= 'd0; end end endmodule
parameter COUNT_MAX = 27000000: COUNT_MAX という名前のパラメータを定義し、初期値を
27000000 に設定。これは、タイマーがオーバーフローするまでのカウント数を指定。
top モジュールで渡された 「MS_PER_STEP * 27000」 の値がここで使用される
logic [$clog2(COUNT_MAX+1)-1:0] は、クロックサイクルをカウントするために使用される。
COUNT_MAX を表現するために必要なビット数を自動的に計算する記述でlog2(n) の底が2の対数
を計算するシステム関数。これによりCOUNT_MAX の値が変わってもカウンタビットを調整できる。
verilog-HDL と SystemVerilogの差の部分
差は以下の4点※④は改定後のVerilog-HDLでも記述可能だが。
①logic データ型を wire および reg に置き換え: 出力ポートは reg 型として宣言し、always ブロック
内で値を代入.。wire は主にモジュール間の接続に使用。
②$clog2() システム関数を手動で計算する: カウンタのビット幅をパラメータで指定するように変更
③ポート接続の簡略記法 (.) を明示的な接続に置き換え
④always_comb 、always_ff を always @() に置き換え
※Verilog HDL (特に IEEE 1364-2001 以降) でも always_ff と always_comb は記述可能だが
//verilog-hdl module top #( // Millisecond per step parameter MS_PER_STEP = 100, // Timer counter width (calculated based on COUNT_MAX) parameter TIMER_COUNT_WIDTH = 25 // Example: ceil(log2(MS_PER_STEP * 27000 + 1)) ) ( input wire clk, output reg phase_a, output reg phase_b ); // Timer wire overflow; timer #(MS_PER_STEP * 27000) timer_i ( .clk(clk), .overflow(overflow) ); // Phase counter reg [1:0] phase_counter = 2'd0; always @ (posedge clk) begin if (overflow) begin phase_counter <= phase_counter + 1'b1; end end // Phase output always @ (phase_counter) begin case (phase_counter) 2'd0: begin phase_a = 1'b1; phase_b = 1'b1; end 2'd1: begin phase_a = 1'b0; phase_b = 1'b1; end 2'd2: begin phase_a = 1'b0; phase_b = 1'b0; end 2'd3: begin phase_a = 1'b1; phase_b = 1'b0; end default: begin phase_a = 1'bx; phase_b = 1'bx; end // Avoid latch endcase end endmodule
//SystemVerilog module top #( // Millisecond per step parameter MS_PER_STEP = 100 ) ( input wire clk, output logic phase_a, output logic phase_b ); // Timer logic overflow; timer #(MS_PER_STEP * 27000) timer_i (.*); // Phase counter logic [1:0] phase_counter = 'd0; always_ff @ (posedge clk) begin if (overflow) begin phase_counter <= phase_counter + 'd1; end end // Phase output always_comb begin case (phase_counter) 0: begin phase_a = 'b1; phase_b = 'b1; end 1: begin phase_a = 'b0; phase_b = 'b1; end 2: begin phase_a = 'b0; phase_b = 'b0; end 3: begin phase_a = 'b1; phase_b = 'b0; end endcase end endmodule
//verilog-hdl module timer #( parameter COUNT_MAX = 27000000 ) ( input wire clk, output reg overflow ); reg [31:0] counter = 32'd0; // Increased width for safety always @ (posedge clk) begin if (counter == COUNT_MAX) begin counter <= 32'd0; overflow <= 1'b1; end else begin counter <= counter + 32'd1; overflow <= 1'b0; end end endmodule
//SystemVerilog module timer #( parameter COUNT_MAX = 27000000 ) ( input wire clk, output logic overflow ); logic [$clog2(COUNT_MAX+1)-1:0] counter = 'd0; always_ff @ (posedge clk) begin if (counter == COUNT_MAX) begin counter <= 'd0; overflow <= 'd1; end else begin counter <= counter + 'd1; overflow <= 'd0; end end endmodule
GitHubでのバージョン管理 備忘録
GitHubでのバージョン管理についてメモを残します。
使用したコマンドは以下の通り。すべて覚えること。
1.git init・・・ルートフォルダに.gitを作成し、Gitでの管理対象とする
2.git add・・・GitHubにプッシュするファイルを選択する。
全て上げる場合は [git add --all]
例えば.pyのものだけプッシュしたい場合は[git add *.py]
3.git reset・・・git addで追加したファイルを外す
全て取り消す場合は [git reset ]
4.git status・・・プッシュするファイルを確認する
5.git commit -m "comment"・・・プッシュするファイルを紐づけする。-m ""はコメント。
6.git log・・・紐づけした履歴を表示する
7.git remote add origin https:GitHub...・・・GitHubにプッシュするリポジトリのURLを選択する
8.git push origin ****(masterなど)・・・ 紐づけしたファイルを選択したリポジトリにアップロード(プッシュ)する
9.git branch・・・リポジトリ内で、ブランチを作成する。現在のブランチを確認する。
10. git checkout ****・・・現在のブランチを移動する。
①Gitをインストール
Gitと同時に「Git Bash」もインストールされる。
・Git BashはcmdなどでGitの機能が使えるコマンドラインツール
・GitはGitHubと連携して、ソースコードをサーバ管理できるツール
②GitHubのユーザー登録をする。
GitHubの「Create Repository」から「リモートリポジトリ」を作成する
③cmdにて管理したいソースコードのフォルダに移動する。
git init
⇒.gitフォルダができる
④git addコマンドにてアップしたいファイルを選択する。
git add *.py などワイルドカードを使って選択することもできる
間違えて加えてしまった場合
git reset ./test2.md などで個別で戻す
またはgit resetで一括で戻す
addしたファイルは、git status で確認できる。
⑤アップしたいファイルが決まれば、git commit -mでコメントを残しつつコミットする
git commit -m "Ver1.0.0"
git log でコミット履歴がわかる

⑥git remote add originでリモートリポジトリに紐づけする
git remote add origin https://github.com/ShigeoYakuno/digital_yatai.git
git remote add origin と アップしたいリポジトリのURL+.git
この時点では、まだアップされない。紐づけされるだけ
⑦GitHubにpushする
git push origin master
ちなみにリモート先とローカルのリポジトリが一致しなかった場合、
error: failed to push some refs to "URL"
が表示される。
個人開発などで、特に問題無い場合は簡単に下記で強制的にプッシュできる
git push --force origin master
■ブランチの操作
ブランチを作成する
⑧git branch Ver1.0.1
ブランチの確認
git branch
⇒*がついて緑色になる方が、現在のブランチ
⑨ブランチの移動
git checkout Ver1.0.1
⇒再度git branchすると、作成したブランチに*がつき、緑色になっている。

⑩ブランチをプッシュする
git push origin Ver1.0.1
⇒GitHubにブランチが作成される。
以上です。
以下は、参考にしたサイトです。
https://www.kagoya.jp/howto/it-glossary/develop/howtousegithub/
https://codelikes.com/git-add/
https://qiita.com/toshi-toma/items/d0efe37edc38f399c6a5
raspberry pi IoT rcloneとGoogle Cloudを使用して、Google Drive上にセンサデータをアップする方法 その② rclone紐づけ偏
①からの続きです。
Google Cloudアカウントなどを作ったPCにrcloneをダウンロードします。
任意のフォルダに解凍します。
ここではd:rcloneとします。
cmdより、同じフォルダに移動して、
rclone authorize "drive" "Client_ID" "Client_secret"と入力します。
IDとsecretは①で取得した値を使用します。

途中でブラウザが立ち上がり、アカウント認証が完了すれば、下記のように
Success!と表示されます。
①で記載しましたが、テストユーザーを一人も追加しない場合は、認証が拒否
されますので、注意が必要です。

cmdのほうにaccess tokenが表示されます。このtokenのためにPCにrcloneを入れました。これより先の作業ではPCは不要です。

access tokenは
Paste the following into your remote machine --->
<---End paste
の間の3行をコピーします。
いよいよraspberry piでの作業です。
インターネットに接続したラズパイより、LXターミナルで下記コマンドを実行します。
少し時間がかかるかもしれませんが、インストールが完了すると思います。
その後
①rclone config
⇒n にて新しい接続を作成します。
name は任意ですが、gdrive、googledriveなどにした方が無難でしょう。
②種類を選択するところ<strage>は、もちろんgoogle driveの番号を入れてください。
rcloneのバージョンによって番号が変わるようです。

③client_ID,crient_secretはその①で取得したIDを使用します。
④scopeは、特にセキュリティなど厳しくなければ「1」でよいでしょう
⑤service account pass は何も入力せず、enterで良いです
⑥edit advanced config はn
⑦use web browser automatically...も n
⑧config_token ここに先程PCで取得したaccess_tokenを入力します
⑨configure this as a shared drive もn
⑩最後、remote接続し続けてOKか?みたいなところ y で終了
これでやっとrcloneとgoogle driveの紐づけが出来ました。
ためしにgoogle driveに何かフォルダを作成した状態で
「rclone lsd gdrive」 と入力し、フォルダが見れるか確認してください。
あとはラズパイのデータをgdriveに送りたい場合は、
rclone copy 送りたいファイルのパス gdrive:googledriveのフォルダ とすれば
送ることが出来ます。
以上
raspberry pi IoT rcloneとGoogle Cloudを使用して、Google Drive上にセンサデータをアップする方法 その① Google Cloud 登録偏
※RaspberryPi以外にアカウント作成や、rcloneとgoogleアカウントを紐づけ認証するためにインターネットに繋がるPC(rcloneをダウンロードすることになります)が1台必要です。
まずはRaspberryPi以外のPCでGoogle CloudでクライアントIDを作成します。
①赤枠部を押して、新しいプロジェクトを選択し、名前を入れて作成します。

次にAPIとサービスから「ライブラリ」を選択します。

ライブラリの中から「Google Drive API」を選択し、「有効」にします。


次に「認証情報」より、「+認証情報を作成」を選択します。

OAuth同意画面が項目にありますので選択し、外部を選択し、作成します。
※OAuthとは、複数のWebサービスを連携して動作させるために使われる仕組みです

途中でメールアドレスを入力する箇所もありますが、google driveと紐づけしたい
アカウントのアドレスでかまいません。
再度認証情報の作成より、今度はOAuthクライアントIDの作成を選択します。

アプリケーションの種類は、「デスクトップアプリ」を選択します。

以上で、クライアントIDとクライアントシークレットの登録が出来ました。
この情報をrcloneに入力する必要があります。

またrcloneをgoogle cloudと紐づけする際、テストユーザーが一人も登録されていなければエラーになります。テストユーザーの登録を行います。こちらもgoogle driveと紐づけしたいアカウントのアドレスでかまいません。

続く。。。
Raspberry Pi 無線でインターネット、有線でリモート接続を行う方法
Raspberry Piにモニターやキーボード、マウスを繋ぐと面倒なので
リモート接続を行うことはよくあると思います。
リモートアプリとしては
・XRDP
・VNC
をよく使うと思います。
またインターネットに接続して、機能を追加することもよくあると思いますが、
何故かラズパイを、「インターネットに接続している無線LANに接続」しつつ、
プライベートIPにて「有線LANでPCとリモート接続」すると、インターネットに
繋がらないことがありました。
1つの解決方法として、eth0のdhcpcd.conf設定を変更することで両方が使えるように
なりましたので、備忘録として記載します。
インターネットへの接続が出来たかは、「ping google.com」でpingが返るかで判断
しました。
eth0のdhcpcd.confの設定を下記の通り変更しました。

static routers とstatic damain_name_servers の値を全て0にしています。
※設定変更後は再起動が必要です。
以上。
ラズパイで 複数のUARTを使用する
・まずはUARTを汎用通信ポートとして使用する に変更
sudo raspi-config
interface option
Serial Port いいえ⇒はい⇒了解
・どのUARTが有効なのかを確認する
ls /dev -l
初期は
serial0 -> ttyS0
で serial0には、ttyS0が割当たっていると思う。
・sudo nano /boot/config.txtの編集
以下の3行を追加。
uart4は、追加したいuartの番号とする。
3つ使いたい場合は、さらに行を追加すればよい。
uart2か4が良いみたい。3と5は癖がありそう。1は最初から有効。

RX23E セルフプログラミング 備忘録 CS+IDEの設定変更偏
RX23Eマイコンのセルフプログラムを実装しています。
はまったところを、備忘録として残します。
環境はCS+です。FITモジュールは使わず(参考程度)、自前で実装しています。
・アプリケーションノート R01AN2184JJ0510 Rev.5.10を参考に下記を追加
①メモリマップの変更
RAMの任意の番地にRPFRAMを追加
ROMの任意の番地にPFRAMを追加

②シンボルファイルの変更
PFRAM=RPFRAMを追加。0~2は自分の環境では最初から入っていた。

以上です。
次はROMからRAMへコピーする箇所を残すつもりです。