My RISC-V debug feature part7 (examine関数(riscv013)の読解, DMの実装)

Sunday, May 16, 2021

今回から、examine関数を読み、初期化のフローを理解する。 DMを実装をして、examineで初期化をできるようにしていく。

examine関数

examine関数はriscv013用のインターフェイスより呼び出される。

src/target/riscv/riscv-013.c

struct target_type riscv013_target = {
	.name = "riscv",

	.init_target = init_target,
	.deinit_target = deinit_target,
	.examine = examine,

	.poll = &riscv_openocd_poll,
	.halt = &riscv_halt,
	.step = &riscv_openocd_step,

	.assert_reset = assert_reset,
	.deassert_reset = deassert_reset,

	.write_memory = write_memory,

	.arch_state = arch_state
};

examine関数の実体

src/target/riscv/riscv-013.c:examine

static int examine(struct target *target)
{
	/* Don't need to select dbus, since the first thing we do is read dtmcontrol. */

	uint32_t dtmcontrol = dtmcontrol_scan(target, 0);
	LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol);
	LOG_DEBUG("  dmireset=%d", get_field(dtmcontrol, DTM_DTMCS_DMIRESET));
	LOG_DEBUG("  idle=%d", get_field(dtmcontrol, DTM_DTMCS_IDLE));
	LOG_DEBUG("  dmistat=%d", get_field(dtmcontrol, DTM_DTMCS_DMISTAT));
	LOG_DEBUG("  abits=%d", get_field(dtmcontrol, DTM_DTMCS_ABITS));
	LOG_DEBUG("  version=%d", get_field(dtmcontrol, DTM_DTMCS_VERSION));

まずは、dtmcontrol_scanにて、DTM内のdtmcsをスキャンする。

dmicsレジスタ

dtmcsレジスタのフォーマットは以下の通り。 アドレス(IR)は0x10である。

各フィールドの意味を以下に示す。(riscv-debug-spec p.88)

dmihardreset

1をこのフィールドに書き込むことで、DTMをハードリセットする。 ハードリセットは実行中のDMIトランザクションを終了させる。また、すべてのDTM内部状態、レジスタを初期状態にする。

dmireset

1を書き込むことで、sticky error状態をクリアする。また、DMIトランザクションはキャンセルしない。

idle

デバッカに与えるヒントで、DMI scan後に必要なRun-Test/Idleサイクル数を保持する。 必要に応じて、デバッカはdmics.dmistatを確認する。

0: 不要
1: 1
2: 2
...

dmistat

0: no error
1: reserved(same as 2)
2: An operation failed
3: An operation was attempted while DMI access was still in progress

abits

The size of address in dmi

version

0: version 0.11
1: version 0.13 and 1.0
15: version not described in any spec

examine続き

src/target/riscv/riscv-013.c:examine

	if (dtmcontrol == 0) {
		LOG_ERROR("dtmcontrol is 0. Check JTAG connectivity/board power.");
		return ERROR_FAIL;
	}
	if (get_field(dtmcontrol, DTM_DTMCS_VERSION) != 1) {
		LOG_ERROR("Unsupported DTM version %d. (dtmcontrol=0x%x)",
				get_field(dtmcontrol, DTM_DTMCS_VERSION), dtmcontrol);
		return ERROR_FAIL;
	}

読み出した、dtmcontrol(dtmcs)を確認する。 riscv013では、versionが1がリセット値であることが保証されているので、dtmcontrolが0であるとき、正しく読み出せていないことになる。

つぎに、dtmcs.versionを読み出し、1(version 0.13 and 1.0)であるか確認している。

src/target/riscv/riscv-013.c:examine

	riscv013_info_t *info = get_info(target);
	/* TODO: This won't be true if there are multiple DMs. */
	info->index = target->coreid;
	info->abits = get_field(dtmcontrol, DTM_DTMCS_ABITS);
	info->dtmcs_idle = get_field(dtmcontrol, DTM_DTMCS_IDLE);

	/* Reset the Debug Module. */
	dm013_info_t *dm = get_dm(target);

読み出した、index, abits, idleinfo構造体へ保存する。

get_info関数

get_info関数では、riscvのバージョン固有のinfo構造体を取得する。 今回の場合は、riscv013用のinfo構造体(riscv013_info_t)を取得する。

src/target/riscv/riscv-013.c

static riscv013_info_t *get_info(const struct target *target)
{
	riscv_info_t *info = (riscv_info_t *) target->arch_info;
	assert(info);
	assert(info->version_specific);
	return (riscv013_info_t *) info->version_specific;
}

src/target/riscv/riscv-013.c riscv013_info_t抜粋

typedef struct {
	/* The indexed used to address this hart in its DM. */
	unsigned index;
	/* Number of address bits in the dbus register. */
	unsigned abits;
	/* Number of abstract command data registers. */
	unsigned datacount;
	/* Number of words in the Program Buffer. */
	unsigned progbufsize;

	/* We cache the read-only bits of sbcs here. */
	uint32_t sbcs;

...

	dm013_info_t *dm;
} riscv013_info_t;

DTMやDMの情報を保持する。

get_dm関数

この関数では、ターゲットのDM構造体を返す。 もし、見つからない場合は、作成し初期化する。 構造体としては、dm013_info_tと同じである。

examine続き

src/target/riscv/riscv-013.c:examine

	if (!dm)
		return ERROR_FAIL;
	if (!dm->was_reset) {
		dmi_write(target, DM_DMCONTROL, 0);
		dmi_write(target, DM_DMCONTROL, DM_DMCONTROL_DMACTIVE);
		dm->was_reset = true;
	}

	dmi_write(target, DM_DMCONTROL, DM_DMCONTROL_HARTSELLO |
			DM_DMCONTROL_HARTSELHI | DM_DMCONTROL_DMACTIVE |
			DM_DMCONTROL_HASEL);
	uint32_t dmcontrol;
	if (dmi_read(target, &dmcontrol, DM_DMCONTROL) != ERROR_OK)
		return ERROR_FAIL;

	if (!get_field(dmcontrol, DM_DMCONTROL_DMACTIVE)) {
		LOG_ERROR("Debug Module did not become active. dmcontrol=0x%x",
				dmcontrol);
		return ERROR_FAIL;
	}

dmが初期化されていない場合は、初期化をする。 初期では、DM_DMCONTROL(DTMでないことに注意)をまず、0で初期化する。 dmi_writedbus(DMI)を経由して、DMのレジスタを書き込みする。 今回は、DM_DMCONTORLアドレスに、0を書き込む。

その後、DMACTIVEフィールドを1にする。 このフィールドはDMのリセット信号として機能する。 このフィールドに書き込みをした場合は、次のアクションを行う前に、書き込んだ値が反映されているかポーリングが必要。 このフィールドが0の場合は、DMはすべて初期状態であることを示し、DMに対するアクセスはすべて失敗する。 また、versionフィールドは正しい値を返さないかもしれない。 1の場合は、DMが正しく動作していることを示す。

次に、DM_DMCONTROL_DMACTIVEの他に、WARLフィールドに-1を書き込む。 読み出したっているビットを見ることで、DMがサポートする機能がわかる。 書き込むフィールドはDM_DMCONTROL_HARTSELDM_DMCONTROL_HARTSELLODM_DMCONTROL_HARTSELHIである。 これらは、hartの選択方法を規定するものである。(詳しくは後ほど)

	dmi_write(target, DM_DMCONTROL, DM_DMCONTROL_HARTSELLO |
			DM_DMCONTROL_HARTSELHI | DM_DMCONTROL_DMACTIVE |
			DM_DMCONTROL_HASEL);

次に、dmi_readにてDM_DMCONTROLを読み出し、DMACTIVEビットが1であるか検査をする。 0の場合は、DMはアクティブでない。

今日はここまで

今回はここまで。 ちなみに、DMACTIVEまでは、DMを実装している。 そのため、examineは途中まで成功している。 次回は、まだ実装していないhartの選択方法から見ていく。 なんか、投稿のフォーマットがぐちゃぐちゃで見づらい説はある。 なんかいい方法ないかな。 あと、表をいい感じに書きたい。

RISC-VRISC-VFPGAJTAG

My RISC-V debug feature part8(implement suggested DMI signal interface)

My RISC-V debug feature part6 (target examine, riscv examine)