ルモーリン

MPU設定関数

投稿:2014-09-10

MPU(メモリ保護ユニット)の設定方法がややこしいので手作業で計算しないでマイコンにやらせてしまおうと考えました。
ソースはこんな感じ。 パラメタは先頭アドレス、長さ、実行可能フラグ、書き込み可能フラグ、読み出し可能フラグを指定すると、ややこしい基準アドレスやリージョンを計算して設定してくれるという訳。 【注意】MPUの仕様外チェックを入れていないのでリージョン/サブリージョンの最小サイズを下回るとアウトです。
// 設定済みのMPUの個数
static uint32_t MpuCount = 0;

void ksrk_mpu(void *pStart, uint32_t lSize, Bool bRun, Bool bWrite, Bool bRead)
{
	KSRK_ASSERT(MpuCount < 8);	// MPUの設定はまだ8個でないはず
	KSRK_ASSERT(0 < lSize); // 長さを指定するはず

	uint32_t lHead = (uint32_t)pStart;

	for (; MpuCount < 8 && 0 < lSize; MpuCount++) {
		// 先頭から最大のサブリージョンの大きさ(ビット数)を求める
		uint32_t i;
		for (i = 0; i < 32; i++) {
			if ((0b1 << i) & lHead) {
				break;
			}
		}
		uint32_t lSubReg_max = i;

		// 設定長さからサブリージョンの大きさ(ビット数)を求める
		for (i = 0; i < 32; i++) {
			if ((0b1 << i) & lSize) {
				break;
			}
		}
		uint32_t lSubReg = i;

		// 最大のサブリージョンを超える場合は最大に合わせる
		if (lSubReg_max < lSubReg) {
			lSubReg = lSubReg_max;
		}

		// リージョンのビット数
		uint32_t lReg = lSubReg + 3;
		uint32_t lReg_size = 0;

		// サブリージョンの実際の大きさ
		uint32_t lSubReg_size = 0b1 << lSubReg;

		// サブリージョンのビットマップ
		uint32_t lSRD = 0b00000000;

		// 先頭から最終サブリージョンまで進める
		uint32_t lSubNo;
		for (lSubNo = (lHead >> lSubReg) & 0b0111; lSubNo < 8; lSubNo++) {
			if (lSubReg_size <= lSize) {
				lSRD |= 0b1 << lSubNo;
				lSize -= lSubReg_size;
				lReg_size += lSubReg_size;
			} else {
				break;
			}
		}
		lSRD = ~lSRD;

		// デフォルトの設定でMPU使用開始
		MPU->CTRL = 0b101;

		// RBARで一緒に設定するので使わない
		// MPU->RNR = 0;

		// リージョンの始点を設定
		MPU->RBAR = 0
				| (lHead & (0xfffffff8 << (lSubReg + 3))) // ADDR
				| 0b1 << 4	// VALID
				| MpuCount << 0	// REGION
				;

		lHead += lReg_size;

		// リージョンの設定と使用
		uint32_t lAP = 0b000;
		if (bWrite) {
			lAP = 0b011;
		}
		else if (bRead) {
			lAP = 0b111;
		}

		MPU->RASR = 0
				| (bRun ? 0b0 : 0b1) << 28	// XN
				| lAP << 24	// AP
				| 0b001 << 19	// TEX
				| 0b0 << 18	// S
				| 0b0 << 17	// C
				| 0b0 << 16	// B
				| lSRD << 8	// SRD
				| (lReg - 1) << 1	// SIZE
				| 0b1 << 0	// ENABLE
				;
	}
}
ありません、ほっ。