Chisel3 Write Vcd file

Chisel3のテストを行ったものの波形が見たくなったりするかもしれないので、
テストしたときにVCDファイルを出力する手順のメモ

main/scala/example/counter.scala

シンプルなカウンターのロジックを書いた

package example

import chisel3._
import chisel3.stage._
import chisel3.stage.ChiselStage

class counter(bitWidth: Int) extends Module {
  val io = IO(new Bundle {
    val out = Output(UInt(bitWidth.W))
    val en = Input(Bool())
  })

  val counter = RegInit(UInt(bitWidth.W), 0.U)

  when(io.en) {
    counter := counter + 1.U
  }.otherwise {
    counter := counter
  }

  io.out := counter;

}

object counterMain extends App {
  val verilogString =
    (new chisel3.stage.ChiselStage).emitVerilog(new counter(32))
}

test/scala/example/counterTester.scala

package example

import chisel3._
import chiseltest._
import chiseltest.internal.WriteVcdAnnotation
import chiseltest.experimental.TestOptionBuilder._
// import treadle.{VerboseAnnotation, WriteVcdAnnotation}
import org.scalatest._
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

class counterTester
    extends AnyFlatSpec
    with ChiselScalatestTester
    with Matchers {
  behavior of "Testers2"

  it should "write vcd output when passing in a WriteVcdAnnotation" in {
    test(new counter(32)).withAnnotations(Seq(WriteVcdAnnotation)) { c =>
      c.reset.poke(true.B)
      c.clock.step()
      c.io.out.expect(0.U)
      c.reset.poke(false.B)
      c.clock.step()
      c.io.out.expect(0.U)
      c.io.en.poke(true.B)
      c.clock.step()
      c.io.out.expect(1.U)
      c.clock.step()
      c.io.out.expect(2.U)
      c.clock.step()
      c.io.out.expect(3.U)
      c.reset.poke(true.B)
      c.io.en.poke(false.B)
      c.clock.step()
      c.io.out.expect(0.U)
      c.reset.poke(false.B)
      c.clock.step()
      c.io.out.expect(0.U)
    }
  }
}

WriteVcdAnnotationを使う
chisel - Generating waveforms with ChiselTest framework - Stack Overflow
chisel-testers2/OptionsPassingTest.scala at 3e3ecc5b25b7b6bc48341ec07c7a54b7ad53bcb7 · ucb-bar/chisel-testers2 · GitHub

sbt test

-DwriteVcd=1

がいるかもしれない?

f:id:katakanan:20210115162359p:plain
pokeによって値がセットされるのはクロックの立下り時になっている。

Chisel3 Getting Started and Simple project

FIRRTLについて知りたかったので、
まずは
sdkman、Java、sbtのインストール
そして、Chisel3 (Scala)をつかった素朴なプロジェクト作成、テスト、Verilog生成の手順メモ

(成熟しきった開発環境というわけではなさそうなので今時点でのとりあえずの手法、常に公式も参照する)
GitHub - chipsalliance/chisel3: Chisel 3: A Modern Hardware Design Language
GitHub - ucb-bar/chisel-testers2: Repository for chisel3 testers2 open alpha
GitHub - freechipsproject/chisel-template: A template project for beginning new Chisel work
freechipsproject/chisel3

環境

WSL2 (Ubuntu 20.04.1 LTS)
fish

(今回は使用していないけど、dockerとか使った方がよかった)

SBTのためのJavaのためのSDKMANを入れる

sudo apt install zip
curl -s https://get.sdkman.io | bash

fishだと用意されたshellscriptが実行できないので
fisherで入れる
【環境構築】fish shellを用いたDebian環境にSDKMANおよびJava(JDK)をインストールする方法

fisher add reitzig/sdkman-for-fish@v1.4.0
sdk selfupdate force
sdk help

でヘルプが出ればOK

SBTのためのJavaを入れる

sbt Reference Manual — Installing sbt on Linux
によると、AdoptOpenJDKのJavaを入れた方がいいらしい
なんかバージョンが違いすぎると問題があるかもしれないので、この記事を書いている時点で上のページに乗っている11.0.x.hs-adptを入れた。
f:id:katakanan:20210111214248p:plain

sdk list java
sdk install java 11.0.9.hs-adpt

sbtのインストール

sdk install sbt

sbtの動作確認

GitHub - ucb-bar/chisel-tutorial: chisel tutorial exercises and answers
ここをビルドできたら、おそらくOK

Getting the RepoとExecuting Chiselをする

sbt run

だけでよい気がする (sbt testは時間かかる上に、失敗した)

テンプレートプロジェクトを使用した

GitHub - freechipsproject/chisel-template: A template project for beginning new Chisel work

ここを好きなプロジェクト名でクローンして、とりあえずtestが通るはず。

git clone https://github.com/ucb-bar/chisel-template.git test00
cd test00
sbt test

VScodeScala拡張機能

VScodeでひらく。
Scala Syntax (official)はSyntax highlightしかしてくれないっぽいので、
Scala (Metals)を入れた。

build.sbtを編集

libraryDependenciesに追加

      "org.scalatest" %% "scalatest" % "3.0.8",
      "org.scalatest" %% "scalatest" % "3.2.0" % "test"

最後に追加

resolvers ++= Seq(
  Resolver.sonatypeRepo("snapshots"),
  Resolver.sonatypeRepo("releases")
)

編集完了すると、以下のようなのが出るので
Import changesしたほうがよさそう。
f:id:katakanan:20210111215456p:plain
f:id:katakanan:20210111215502p:plain
(コード補完してくれそう)

.gitignoreにいれた

*.db
*.fir
*.v
.bloop

ディレクトリ構成

f:id:katakanan:20210111215909p:plain
templateにあった構成をまねたほうがよい。

main.scala

package example

import chisel3._
import chisel3.stage._

class main extends Module{
    val io = IO(new Bundle{
        val A = Input(UInt(1.W))
        val B = Input(UInt(1.W))
        val C = Output(UInt(1.W))
    })

    io.C := io.A & io.B
}

テストだけならこれだけでもできた。
Verilogも出すなら追記が必要(後述)。

mainTester.scala

日本語で検索して出てくる記事はchisel-iotestersとかいうライブラリを使っていてpeekpoketestを継承してpeek, pokeしているが、
templateのプロジェクトがchisel-tester2なるものを使用していたのでそれで書いてみた。
GitHub - ucb-bar/chisel-testers2: Repository for chisel3 testers2 open alpha
まだalphaバージョンらしいので一部の関数以外は大きく変更される可能性がある。
chisel-testers / iotestersから将来的に移行するらしい

These APIs may be considered stable and are unlikely to change significantly:

Test invocation test
Basic operations: poke, peek, expect, step, including on Bundles
Basic concurrency operations: fork, join
Decoupled library: enqueueNow, expectDequeueNow, their sequence variants, expectPeek, expectInvalid - though the names may be refactored
timescope - though the name may be refactored

package example

import org.scalatest._
import chiseltest._
import chisel3._
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

class mainTester extends AnyFlatSpec with ChiselScalatestTester with Matchers {
    it should "test: and circuit" in {
        test(new main){c => 
            c.io.A.poke(0.U)
            c.io.B.poke(0.U)
            c.clock.step()
            c.io.C.expect(0.U)

            c.io.A.poke(1.U)
            c.io.B.poke(0.U)
            c.clock.step()
            c.io.C.expect(0.U)

            c.io.A.poke(0.U)
            c.io.B.poke(1.U)
            c.clock.step()
            c.io.C.expect(0.U)

            c.io.A.poke(1.U)
            c.io.B.poke(1.U)
            c.clock.step()
            c.io.C.expect(1.U)
        }
    }
}

AND回路なのでclockの記述はいらないけど一応。
これで

sbt test

すると、
f:id:katakanan:20210111222124p:plain

わざと失敗するようにして、
f:id:katakanan:20210111222221p:plain
f:id:katakanan:20210111222419p:plain

Verilogの出力

main.scalaに追加

object mainDriver extends App {
    chisel3.Driver.execute(args, () => new main)
}

これで

sbt run

すると、
main.firとmain.vができていた。

;buildInfoPackage: chisel3, version: 3.4.1, scalaVersion: 2.12.12, sbtVersion: 1.3.10
circuit main : 
  module main : 
    input clock : Clock
    input reset : UInt<1>
    output io : {flip A : UInt<1>, flip B : UInt<1>, C : UInt<1>}
    
    node _io_C_T = and(io.A, io.B) @[main.scala 13:18]
    io.C <= _io_C_T @[main.scala 13:10]
   
module main(
  input   clock,
  input   reset,
  input   io_A,
  input   io_B,
  output  io_C
);
  assign io_C = io_A & io_B; // @[main.scala 13:18]
endmodule

chisel3.Driver.executeはDeprecatedらしいので、現時点ので推奨らしき書き方は、

    // chisel3.Driver.execute(args, () => new main)
    val verilogString = (new chisel3.stage.ChiselStage).emitVerilog(new main)
    println(verilogString)

コードが表示されて、なおかつ、ファイルが生成される。
GitHub - katakanan/chisel3_simple_project

Scalaは初めてだが、Chisel自体はHLSなどとは違って少しだけレジスタを意識して書くようなそんなところもありそうなので
普通のソフトウェア言語のように書く、というわけにはまだまだいかなさそう。

Rust レイトレーシング

e-tipsmemo.hatenablog.com
書きながら再び書き直した。

f:id:katakanan:20200820213000p:plain

Thu Aug 20 20:06:12 JST 2020
Hello World
1600x800
thread  : 12
ss      : 100

________________________________________________________
Executed in  143.56 secs   fish           external
   usr time  1384.58 secs    0.00 micros  1384.58 secs
   sys time    0.92 secs    0.00 micros    0.92 secs

Thu Aug 20 20:08:36 JST 2020

その中で色々あったことを書く。

Chapter 5:Surface normals and multiple objects

        if d > 0.0 {
            let tmp = (-b - d.sqrt()) / (2.0 * a);
            if t0 < tmp && tmp < t1 {
                let p = r.point_at_paramete(tmp);
                return Some(HitRecord {
                    t: tmp,
                    p: p,
                    n: (p - self.center).normalize(),
                });
            }
        }

直線と球の交点が2つ存在してもう片方を考慮するのを忘れていた。(本をちゃんと見ていなかった)
もう一つの交点を忘れると、あとの方でガラス球の反射がおかしくなる。

Chapter 7: Diffuse Material

Diffuse Materialを実装して、ガンマ補正をしていないとかなり暗なると書いてある。
本に掲載されている画像もかなりくらい。
しかし実装したら暗くなかった。
f:id:katakanan:20200820223652p:plain
gamma = 0.5だと明るすぎる気がするので0.65に。
image crateが悪い?

Chapter 8: Metal / Chapter 9:Dielectrics

これまた球の交差計算で
HitRecordの法線をnormalize(正規化)で求めている。

        if d > 0.0 {
            let sqrt_discriminant = d.sqrt();
            let tmp = (-b - sqrt_discriminant) / (2.0 * a);
            if t0 < tmp && tmp < t1 {
                let p = r.point_at_paramete(tmp);
                return Some(HitRecord {
                    t: tmp,
                    p: p,
                    n: (p - self.center).normalize(),
                    mat: self.mat.clone(),
                });
            }

            let tmp = (-b + sqrt_discriminant) / (2.0 * a);
            if t0 < tmp && tmp < t1 {
                let p = r.point_at_paramete(tmp);
                return Some(HitRecord {
                    t: tmp,
                    p: p,
                    n: (p - self.center).normalize(),
                    mat: self.mat.clone(),
                });
            }
        }

これは法線というベクトルを計算する意味では間違っていないと思うが、、
レイトレーシング的にはマイナスの半径を持つ球体を用意したりするらしいので、そのときに計算がおかしくなる。
(法線の符号が逆になる)

normalizeは normで割っているので、正の値で割ることになるが、
この本のプログラムでは 球の半径で割るのが正しい(マイナスになり得る)。
ので、こう。

n: (p - self.center) / self.radius,

その他

マルチスレッドで色々と面倒だった気がする(忘れ)
GitHub - katakanan/raytrace_one_week


参考

Headless modeのJetsonnanoのデスクトップにVNCでアクセス

全く更新していなかったので
適当に記事を書く。

Displayがつながれていない、SSH接続のみができる、Jetsonnanoに対してVNC Viewerでアクセスするための設定で調べたことをまとめる。

Headless mode
Without monitorなど

$ lsb_release  -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.4 LTS
Release:        18.04
Codename:       bionic

調べると出てくる記事

Setting up VNC server (Solved) - Jetson AGX Xavier - NVIDIA Developer Forums
Vino - ArchWiki
Jetson Nanoにリモートデスクトップ(VNC)環境を用意する - Qiita
Ubuntu18.04 LTS GNOME環境でVNCサーバーを立てる - Qiita
Jetson NanoでHeadlessセットアップを行う - Qiita

デスクトップを操作するには、Display:0に接続する必要がある。
TigerVNCを使う方法と、Vinoを使う方法がある。
Vinoを入れた。

手順

Nvidia forumのポストと、Arch wikiより。

$ sudo apt update
$ sudo apt install vino

自動起動のために
~/.config/autostart/vino-server.desktop

[Desktop Entry]
Type=Application
Name=Vino VNC server
Exec=/usr/lib/vino/vino-server
NoDisplay=true
$ gsettings set org.gnome.Vino prompt-enabled false
$ gsettings set org.gnome.Vino require-encryption false
$ sudo reboot

VNCでアクセスする際のパスワードをコマンドラインから設定する方法は、

$ gsettings set org.gnome.Vino vnc-password $(echo -n "mypassword"|base64)

の mypassword部分を変更する。

f:id:katakanan:20200322203839p:plain

f:id:katakanan:20200322204035p:plain

(モニターがあったとしても?)Desktop Sharingがフリーズする

Ubuntu Internal error when selecting desktop sharing in settings. - Jetson Nano - NVIDIA Developer Forums
にある、2019/Apr/24の投稿にもあるが、XMLに追記して、 glib-compile-schemasを行うことが、Desktop Sharingパネルがクラッシュする問題の回避方法として書かれている。
試してみたが、クラッシュした。

www.switch-science.com

Rust serialport ③

e-tipsmemo.hatenablog.com
以前このようなプログラムを書いて、
ワットチェッカーからシリアルポート(over Bluetooth)経由で電力のログを取っていたが、
まともにエラーハンドリングしていなかったので、
不具合が起きており、その修正を行った。

問題の処理

pub fn communicate_command(&mut self, cmd:Vec<u8>, rcount:usize)->Result<Vec<u8>, std::io::Error>
{
	let mut resp : Vec<u8> = vec![];

	match self.port.write(&cmd) {
		Ok(_) => (),
		Err(e) => eprintln!("{:?}", e),
	}

	let mut buffer : [u8; 1] = unsafe{mem::uninitialized()};

	loop{
		match self.port.read(&mut buffer)
		{
			Ok(bytes) => if bytes == 1
			{
				resp.push(buffer[0]);
				if resp.len() == rcount 
				{
					break;
				}
			},
			Err(ref e) if e.kind() == io::ErrorKind::TimedOut => (),
			Err(e) => return Err(e),
		}
	}
	
	Ok(resp)
}

指定したバイト数を受信するまでループするが、何らかの理由で受信バイト数が来ずにずっとループが回っていた。

Err(ref e) if e.kind() == io::ErrorKind::TimedOut => {
	eprintln!("{:?}", e);
	timeoutbreak = timeoutbreak + 1;
	
	if timeoutbreak == 200 {
		return Err(Error::new(ErrorKind::Other, "timeout 200 times"));
	}
	
	()
},

一回ごとのタイムアウトの時間は10ミリ秒
連続して、200回タイムアウトしたら、Errでループを抜けることにした。
(計測の間隔が2秒であることと、RTCを設定するのに1300ミリ秒ぐらいかかるので)

f:id:katakanan:20191017061210p:plain
処理を継続させる代わりにstd::process::exitで止めたところ。

止めずに再び新しい測定値を得るようにしたところどうやら動き続けているのでこれでとりあえず放置。

あと、mem::uninitialized()が非推奨になっていた。

実践Rust入門[言語仕様から開発手法まで]

実践Rust入門[言語仕様から開発手法まで]

ThingsBoardを利用する 3.

Thingsboardに送る適当なプログラムは書いたので
Thingsboardのグラフ画面を作成する。

とは言ってもここの動画で行っていることをアレンジするだけ
Getting Started | ThingsBoard


複数のセンサーが増えてきたときのために
動画内では、
defaultダッシュボードにはセンサーの現在状態だけを一覧表示するようなサマリー画面にして
それぞれのセンサーの行をクリックしたときの動作を定義して、詳細を見に行くという設定にするようにしている。

サマリー表示ウィジェットの編集から。
f:id:katakanan:20191010040415p:plain

行をクリックしたときの動作を定義する。
f:id:katakanan:20191010040530p:plain

できたもの

Chartウィジェットには最初から平均値などを表示してくれていた。
f:id:katakanan:20191010040642p:plain

その他

おそらく上級設定のJavascriptのところをどうにかすれば、電力の積分値を計算して電力料金なども算出することができるだろう。
いつかやる
f:id:katakanan:20191010040909p:plain

とりあえずやりたいことの9割程度はできたので、
Rustの電力を取ってくるソフトウェアをdaemon化するかどうにかしようと検討している。
(あとはthingsboard serviceが時々勝手に落ちてしまっている原因などを調査する?(メモリが少ないからのような気がする))

おまけ

起動してから一時間程度視聴した。
f:id:katakanan:20191010051216p:plain

PLEX 地上デジタル・BS・CS対応TVチューナー PX-W3PE4

PLEX 地上デジタル・BS・CS対応TVチューナー PX-W3PE4

ThingsBoardを利用する 2.

今回は、Rustのプログラムを書いて、
実際のデータを使って、何秒か一回にデータが送られるようにする。

reqwest

e-tipsmemo.hatenablog.com
前回はreqwestを使って単発で送ったので
今回は昔の電力測定プログラムを合わせるだけ。

fn main() {
    println!("Hello, world!");
    let mut wc = WattChecker::default();
    wc.init();
    wc.start_measure();

    let running = Arc::new(AtomicBool::new(true));
    let r = running.clone();
    ctrlc::set_handler(move || {
        r.store(false, Ordering::SeqCst);
    }).expect("Error setting Ctrl-C handler");

    let token = " ************** "; //デバイスのアクセストークンキーを入れる
    let str_url = format!("http://192.168.0.14:8080/api/v1/{}/telemetry", token);
    let url = reqwest::Url::parse(&str_url).unwrap();
    let client = reqwest::Client::new();
   
    while running.load(Ordering::SeqCst)
    {
        match wc.request_measure() {
            Ok(cmd) => {
                disp_data(&cmd);
                let map = parse_data(&cmd);

                let res = client
                    .post(url.clone())
                    .json(&map)
                    .send()
                    .unwrap();

                println!("{}", res.status());
            
            },
            Err(e) => eprintln!("{:?}", e),
        }

        thread::sleep(Duration::from_millis(2000));
    }

    println!("stop measuring");
    wc.stop_measure();
}

結果

f:id:katakanan:20191006054339p:plain
どうやらHTTPのリクエストが遅くなって前の測定から2秒以上かかってしまうことがあるらしいので、
これは非同期に処理しなければいけないのかもしれない。
あとリクエストを投げ続けていると、とまってしまう。

とりあえず次は、thingsboardのログ表示部分を作る。