コグノスケ


2023年4月7日

NSITEXE NS31A on Arty A7にOpenOCDで接続

目次: OpenOCD - まとめリンク

Arty A7に書き込んだNS31AにOpenOCDで接続する方法についてメモしておきます。

NS31A用の設定

現在OpenOCDにNS31A用のコンフィグは登録されていませんので、手動で下記のファイルを作ります。

先頭のadapter speedはJTAGとボード間の信号の周波数です。J-Linkの場合15000 (= 15MHz) が最大値で、周波数が高い方が実行ファイルの転送速度などの面で有利です。が、家の機材で試したところ12MHzくらいで限界のようで、15MHzだとデバッグレジスタが読めないなどのエラーが発生しました。原因は追っていませんがJ-LinkとArty A7の間をバラバラの線で繋いでいるせいかもしれません……。

openocd-ns31.cfg

reset_config trst_and_srst
adapter speed 1000

set _CHIPNAME riscv
set _DAP_TAPID 0x400059df
set _ENDIAN little
set _TARGETNAME $_CHIPNAME.cpu.0

jtag newtap $_CHIPNAME dap -irlen 5 -ircapture 0x01 -irmask 0x03 -expected-id $_DAP_TAPID

target create $_TARGETNAME riscv -endian $_ENDIAN -chain-position $_CHIPNAME.dap -coreid 0

ファイル名は何でも良いですが、ここではopenocd-ns31.cfgとします。ファイルはOpenOCDから見える場所に置いてください。今回はOpenOCDのソースコードがあるディレクトリの直下に置いています。

接続

私の持っているJTAGインタフェースはSEGGER J-Link(J-Link - Model Overview - SEGGER)なので、tcl/interface/jlink.cfgを使用します。他のJTAGをお使いの方は適切なコンフィグファイルに読み替えてください。

OpenOCDの -fオプションにjlink.cfgと先ほど作ったopenocd-ns31.cfgを指定します。もし他マシンからネットワーク経由でOpenOCDに接続したければ -c 'bindto 0.0.0.0' も指定します。

OpenOCDの起動ログ例
$ ./src/openocd -c 'bindto 0.0.0.0' -f tcl/interface/jlink.cfg -f ./openocd-ns31.cfg

Open On-Chip Debugger 0.11.0+dev-00529-gb1de11616 (2021-12-04-17:24)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "jtag". To override use 'transport select <transport>'.0
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : J-Link V11 compiled Jul  3 2020 10:47:34
Info : Hardware version: 11.00
Info : VTarget = 3.322 V
Info : clock speed 1000 kHz
Info : JTAG tap: riscv.dap tap/device found: 0x400059df (mfg: 0x4ef (NSITEXE Inc), part: 0x0005, ver: 0x4)
Info : datacount=2 progbufsize=2
Info : Examined RISC-V core; found 1 harts
Info :  hart 0: XLEN=32, misa=0x40101125
Info : starting gdb server for riscv.cpu.0 on 3333
Info : Listening on port 3333 for gdb connections

特にエラーもなく起動したら、あとはHiFiveなどと同じでGDBなどから接続&デバッグ可能です。

編集者:すずき(2023/09/24 09:18)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2023年4月10日

Linuxまとめリンク

目次: Linux - まとめリンク

カーネル、ドライバ関連。

ユーザーランド、その他。

編集者:すずき(2023/09/24 13:39)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2023年4月22日

車のバッテリー完全に死亡

目次: 車 - まとめリンク

もはや毎年恒例レベルですが、車のバッテリーが完全に干上がって死にました。


車のバッテリーの電圧

電圧は1.5Vでした。乾電池か?

ジャンプスターター購入

自宅の駐車場でバッテリー上がり、などというくだらない理由で幾度となくJAFを呼ぶのがいい加減恥ずかしくなってきたので、先日KashimuraのジャンプスターターKD-238(メーカーサイトへのリンク)を買いました。

ジャンプスターターは車がないと無意味な代物ですが、その点KD-238はモバイルバッテリーとしても使えるので安心です。普通のモバイルバッテリーと比べるとゴツいですが、そこはご愛敬で。

早速買った後にジャンプスタートを試したんですけど、エンジン掛からなかったんですよ……。説明書に「ジャンプスタートは一度に2回までにしておけ」とあったので、素直に従ってその日は諦めました。

再チャレンジ

今日もダメ元で、ジャンプスタートにチャレンジしてみたところ、あっさりエンジンが掛かりました。キーを回す前にアクセルをバシバシ連打したのが良かったんでしょうか?先日も今日もECUのエラーなどは出ていなかったため、原因は皆目見当が付きません。不思議ですね??

エンジン始動直後を見ると、電圧は14V超(充電しているときの電圧)、クランプメーターで電流を見ると2.6Aで、一応バッテリーは充電されているようですが、充電がわずか5分くらいで止まってしまいます。これはダメそう。


バッテリー充電時の電流

さらにバッテリー端子の周りを良く見ると、白い粉を吹いています。雨で濡れた?ような跡にも見えますが、バッテリーの上面にはカバーがあるので雨は掛からないはずです。


バッテリー端子の周りが粉っぽい

これはまさか過放電の影響か何かでバッテリー液漏れしたんでしょうか?もう色々ダメですね。諦めてバッテリー交換か……あーあ。

バッテリー交換

ちょうど今日はスバルのディーラーで半年点検する日だったので、ディーラーに何とかしてもらうために走り始めました。走っている最中にも関わらずバッテリー電圧は非常に不安定で、12Vを突然下回るなど挙動不審です。交差点で止まるとエンジンの回転数が落ちてガタガタとエンストしかけていました。環八でエンストとかやめて、怖い。

当然ながら、ディーラー整備士のお兄さんに「バッテリーがかなり消耗しています、交換をお勧めします」と言われました。ソウデスネ……。素直にバッテリー交換してもらい26,000円くらいの出費でした。痛え出費です。

何回目のバッテリー交換だ

メモにあるだけでも 2007年11月17日2013年3月20日2016年7月24日2020年7月28日2022年1月10日にバッテリー交換しています。これで6回目です。バッテリーだけで多分20万円くらい無駄にしています。

私は生活サイクル的には、明日いきなり車がなくなっても全く困りません……。でも近所の駐車場は車がたくさん停まっている=つまり東京で車を持って暮らしている人は意外といるようです。みなさん一体何に使っているんでしょうね……??

編集者:すずき(2023/09/30 14:45)

コメント一覧

  • hdkさん(2023/04/24 21:35)
    えっ、車は普通に買い物とかお出かけとか色々使いますよね??? 2週間にいっぺんぐらい乗っていれば普通は上がることはないと思いますが...

    バッテリーが粉を吹くのは古いバッテリーあるあるですね。あっ、古いわけではないのか、使いすぎてもそうなるんですね。別に粉があっても充電できているバッテリーはあるんですが、さすがに充電されないのは使えませんね :D
  • すずきさん(2023/04/25 12:45)
    いやー、全然使わないんですよね、これが……。駐車場がちょっと遠いのもあって、面倒くさくて歩いてしまいます。
    もっと無駄に乗ったほうが良いのかなあ。
open/close この記事にコメントする



2023年4月24日

Arty A7のFPGAを書き換える方法

目次: RISC-V - まとめリンク

いつもArty A7-100を書き換えるときSPI Flashの型番を忘れるので、手順と一緒にメモしておきます。

Arty A7にはXilinx Artix-7 (XC7A100TCSG324-1) というFPGAが搭載されています。XilinxのFPGAを扱うにはVIVADOというツールが必要で、このツールを使うためには有料ライセンスが必要です。が、FPGAに回路を書き込むだけなら無料で使用可能なLab Editionが使えます。起動したらOpen Hardware Managerを選択します。


Xilinx VIVADO Lab Edition起動画面

Open TargetとAuto Connectを選択します。PCにボードを複数接続している場合は、Arty A7が選ばれるとは限らないので、Open New Targetを選択してウィザードを進める必要があるかもしれません。


Open Target

通常FPGAの回路はFPGA内のRAMに書かれるため、揮発性つまり電源を切ると消えます。しかしConfiguration用のSPI Flashに回路を書くと、次回以降のボード電源投入時にFPGAがSPI Flashから自動的に回路をロードしてくれる仕組みです。

Configuration用のSPI Flashに書き込むには、VIVADOに対してボード上のSPI Flash品番を教える必要があります。Add Configuration Memory Deviceを選択しましょう。


Add Configuration Memory Device

Arty A7のSchematics(回路図)を見るとInfineon TechnologiesのS25FL128Sという型番のSPI Flashが搭載されているようです。VIVADOのSearchボックスに型番を途中まで入れると、それらしい選択肢が表示されます。


Arty A7搭載のSPI Flashの型番


VIVADOのSPI Flash型番サーチ

Configuration Memory Deviceを追加出来たら、Program Configuration Memory Deviceを選択します。ダイアログに回路データ(*.mcs)ファイル名を入れて、FPGAの回路をボード上のSPI Flashに書き込みます。


回路データの書き込み

書き込みには数分掛かるはずです。回路データを書き込んだら、ボードの電源を一度切って再投入しましょう。書き込んだ回路データが反映されるはずです。

編集者:すずき(2023/04/26 00:12)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2023年4月28日

Linuxの /dev/zeroの実装

目次: Linux - まとめリンク

Twitterで/dev/zeroの話をしている人が居て、そういえばLinuxはどこで実装しているのかうろ覚えだったので、コードを調べつつメモしておきます。

デバイスメジャー番号の調べ方

デバイスファイル /dev/zeroをls -lで見ると、キャラクターデバイスであること、メジャー番号1、マイナー番号5だと分かります。

/dev/zeroをlsで調べた結果
$ LANG=C ls -la /dev/zero

crw-rw-rw- 1 root root 1, 5 Mar 27 23:22 /dev/zero
↑                    ↑
↑                    デバイスメジャー番号1、マイナー番号5
c: キャラクターデバイス

Linuxの場合/proc/devicesにデバイスメジャー番号の一覧があります。

/proc/devicesの中身
$ head /proc/devices

Character devices:
  1 mem    ★★キャラクターデバイス、メジャー番号1 = memドライバ★★
  4 /dev/vc/0
  4 tty
  4 ttyS
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
  7 vcs
 10 misc

メジャー番号1を使用しているmemというドライバを調べれば良いことが分かりました。

ドライバの実装箇所

大抵はドライバ名でgrepすると発見できますが、memという単語はいたるところに使われていて探しにくいと思います。幸いながらmemはメジャー番号が常に1に固定されたドライバなので、メジャー番号を頼りに探した方が良いでしょう。

メジャー番号1を使用するドライバ

// linux/include/uapi/linux/major.h

#define MEM_MAJOR		1


// linux/drivers/char/mem.c

static int __init chr_dev_init(void)
{
	int minor;

	if (register_chrdev(MEM_MAJOR, "mem", &memory_fops))    //★★キャラクターデバイス、メジャー番号1 = memとして登録★★
		printk("unable to get major %d for memory devs\n", MEM_MAJOR);

メジャー番号の謎は解けて、実装箇所がdrivers/char/mem.cであることがわかりました。

マイナー番号の役割

次に気になるのは/dev/zeroも /dev/nullも同じメジャー番号1ですが、どう区別するか?です。メジャー番号は同じでも、マイナー番号は異なっていて /dev/zero = 5, /dev/null = 3です。マイナー番号をどこで見ているか、何に使っているか調べます。

open関数とマイナー番号

// linux/drivers/char/mem.c

static const struct file_operations memory_fops = {
	.open = memory_open,    //★★メジャー番号1のキャラクタデバイスファイルを開く時に呼ばれる関数★★
	.llseek = noop_llseek,
};

static int memory_open(struct inode *inode, struct file *filp)
{
	int minor;
	const struct memdev *dev;

	minor = iminor(inode);
	if (minor >= ARRAY_SIZE(devlist))
		return -ENXIO;

	dev = &devlist[minor];    //★★デバイスマイナー番号をみて操作を決める★★
	if (!dev->fops)
		return -ENXIO;

	filp->f_op = dev->fops;
	filp->f_mode |= dev->fmode;

	if (dev->fops->open)
		return dev->fops->open(inode, filp);

	return 0;
}

マイナー番号からdevlistの何番目の要素を使うか決めているようです。devlistは何かというと、

memドライバのdevlist変数の定義

// linux/drivers/char/mem.c

static const struct memdev {
	const char *name;
	umode_t mode;
	const struct file_operations *fops;
	fmode_t fmode;
} devlist[] = {
#ifdef CONFIG_DEVMEM
	 [DEVMEM_MINOR] = { "mem", 0, &mem_fops, FMODE_UNSIGNED_OFFSET },
#endif
	 [3] = { "null", 0666, &null_fops, FMODE_NOWAIT },
#ifdef CONFIG_DEVPORT
	 [4] = { "port", 0, &port_fops, 0 },
#endif
	 [5] = { "zero", 0666, &zero_fops, FMODE_NOWAIT },     //★★minor 5 = /dev/zero★★
	 [7] = { "full", 0666, &full_fops, 0 },
	 [8] = { "random", 0666, &random_fops, FMODE_NOWAIT },
	 [9] = { "urandom", 0666, &urandom_fops, FMODE_NOWAIT },
#ifdef CONFIG_PRINTK
	[11] = { "kmsg", 0644, &kmsg_fops, 0 },
#endif
};

...

static const struct file_operations zero_fops = {
	.llseek		= zero_lseek,
	.write		= write_zero,
	.read_iter	= read_iter_zero,
	.read		= read_zero,    //★★read関数★★
	.write_iter	= write_iter_zero,
	.mmap		= mmap_zero,
	.get_unmapped_area = get_unmapped_area_zero,
#ifndef CONFIG_MMU
	.mmap_capabilities = zero_mmap_capabilities,
#endif

マイナー番号をインデックスとした様々なデバイスを定義しています。今回はインデックス5, "zero" (= /dev/zero) をさらに追います。

/dev/zeroの正体

ゼロを返すとはすなわち、読み出す(= readを呼ぶ)とバッファの中身にゼロが書かれて戻ってくることを意味します。/dev/zeroのread関数であるread_zero() を見ると、

/dev/zeroのread関数

// linux/drivers/char/mem.c

static ssize_t read_zero(struct file *file, char __user *buf,
			 size_t count, loff_t *ppos)
{
	size_t cleared = 0;

	while (count) {
		size_t chunk = min_t(size_t, count, PAGE_SIZE);
		size_t left;

		left = clear_user(buf + cleared, chunk);    //★★バッファにゼロを書いている★★
		if (unlikely(left)) {
			cleared += (chunk - left);
			if (!cleared)
				return -EFAULT;
			break;
		}
		cleared += chunk;
		count -= chunk;

		if (signal_pending(current))
			break;
		cond_resched();
	}

	return cleared;
}

バッファをゼロクリアしているのはclear_user() という関数のようです。この関数はアーキテクチャによって処理が異なります。一例としてRISC-Vの実装を見ましょう。

clear_user() 関数RISC-V向けの実装

// linux/arch/riscv/include/asm/uaccess.h

static inline
unsigned long __must_check clear_user(void __user *to, unsigned long n)
{
	might_fault();
	return access_ok(to, n) ?
		__clear_user(to, n) : n;
}


// linux/arch/riscv/lib/uaccess.S

	.macro fixup op reg addr lbl
100:
	\op \reg, \addr
	_asm_extable	100b, \lbl
	.endm


ENTRY(__clear_user)

	/* Enable access to user memory */
	li t6, SR_SUM
	csrs CSR_STATUS, t6    //★SUM (permit Supervisor User Memory access) をセットしている★
                               //★詳細はriscv-privilegedの3.1.6.3 Memory Privilege in mstatus Registerを参照★

	add a3, a0, a1
	addi t0, a0, SZREG-1
	andi t1, a3, ~(SZREG-1)
	andi t0, t0, ~(SZREG-1)
	/*
	 * a3: terminal address of target region
	 * t0: lowest doubleword-aligned address in target region
	 * t1: highest doubleword-aligned address in target region
	 */
	bgeu t0, t1, 2f
	bltu a0, t0, 4f
1:
	fixup REG_S, zero, (a0), 11f    //★★メインのループ処理★★
	addi a0, a0, SZREG
	bltu a0, t1, 1b
2:
	bltu a0, a3, 5f

3:
	/* Disable access to user memory */
	csrc CSR_STATUS, t6
	li a0, 0
	ret
4: /* Edge case: unalignment */
	fixup sb, zero, (a0), 11f
	addi a0, a0, 1
	bltu a0, t0, 4b
	j 1b
5: /* Edge case: remainder */
	fixup sb, zero, (a0), 11f
	addi a0, a0, 1
	bltu a0, a3, 5b
	j 3b

	/* Exception fixup code */
11:
	/* Disable access to user memory */
	csrc CSR_STATUS, t6
	mv a0, a1
	ret
ENDPROC(__clear_user)
EXPORT_SYMBOL(__clear_user)


// linux/arch/riscv/include/asm/asm.h

#define REG_S		__REG_SEL(sd, sw)    //★64bitならsd, 32bitならsw★


//★アセンブラの場合aがそのまま出力、Cの場合は文字列として出力★
//  asm: __ASM_STR(abc) -> aaa
//  C: __ASM_STR(abc) -> "aaa"
#if __riscv_xlen == 64
#define __REG_SEL(a, b)	__ASM_STR(a)
#elif __riscv_xlen == 32
#define __REG_SEL(a, b)	__ASM_STR(b)
#else
#error "Unexpected __riscv_xlen"
#endif


#ifdef __ASSEMBLY__
#define __ASM_STR(x)	x    //★アセンブラなのでこちら★
#else
#define __ASM_STR(x)	#x
#endif

アセンブラが出てきて面喰らいますが、基本的にはバッファにゼロをストアする処理です。memset() と大きく異なる点はmstatus.SUMをセットしていることです。RISC-VではSUMをセットしないとS-mode (Supervisor mode) からU-mode (User mode) のメモリにストアできない(ストアアクセスフォルトが発生)ためです。

編集者:すずき(2023/04/29 23:28)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



こんてんつ

open/close wiki
open/close Java API

過去の日記

open/close 2002年
open/close 2003年
open/close 2004年
open/close 2005年
open/close 2006年
open/close 2007年
open/close 2008年
open/close 2009年
open/close 2010年
open/close 2011年
open/close 2012年
open/close 2013年
open/close 2014年
open/close 2015年
open/close 2016年
open/close 2017年
open/close 2018年
open/close 2019年
open/close 2020年
open/close 2021年
open/close 2022年
open/close 2023年
open/close 過去日記について

その他の情報

open/close アクセス統計
open/close サーバ一覧
open/close サイトの情報