2009年10月アーカイブ



ロガー続き。GPSのデータは$GPGGAから緯度、経度、高度だけ抜きだして、それぞれ4byteにして有効なデータの時だけSDカードに書きだすことにしました。
  Global Positioning System Fix Data
$GPGGA,084250.694,3539.4058,N,13939.2779,E,1,04,04.2,00039.2,M,0039.4,M,000.0,0000*49

$GPGGA,
084250.694, UTC
3539.4058,N,
13939.2779,E,
1,		Quality (0: can't receive)
04,		# of Satellites
04.2,		HDOP
00039.2,M,	altitude
0039.4,M,	altitude WGS-84
000.0,		Data age
0000		DPGS ID
*49		checksum
そして、ホスト側でSDカードのデータをまたNMEA-0183形式に変換。
GPSレシーバユニットは基板から外して、ケースの上にしました。これなら十分 にGND層もあるし、感度あがるかな...と思いきや、多少良くなった程度でした。


このロガーを持ってチャリンコで町内一周。データを変換して、カシミール3Dに 喰わせてみると...
おおお。結構感動。

LCDは小さいのが幸いして配置が楽だ。このあたりでいいかな。文字の大きさも 大丈夫そうだ。欲を言えばもうちょっとボールドにしたい。
次の走行のロガーの準備は終了。さっさとマシン整備せねば。



菜園状況。ニンジンの間引きをしました。根の伸びがいい気がする。



ロガー続き。GPSまわり。GPS-52モジュールからはNMEA-0183フォーマットでシ リアルに送られてくる。これをパースして必要なデータだけパックしてファイ ルに書き込みたい。まずはパース。パースは苦手だ。なんかいつも場当たり的 なコードになってしまう。
ここはマイOSだ。富豪的にファイバを使おう!ということにしたのだけど、やっ ぱりこれは自分で自分の足を撃ち抜くような機構だ。デバッグするのに脳ミソ もつれるかと思った。やれるからといってやってはいけない悪夢のようなコー ド。とはいえおもしろい.
ここでfiber_yield, fiber_return_parentは
これはARM
FUNC (fiber_switch)
	//
	// save context. (from)
	stmia	r0!,	{r4-r14}
	// subroutine return value/1st arg
	mov	r0,	r2
	ldmia	r1,	{r4-r14}
	mov	pc,	lr
これはH8
	.globl _fiber_switch
_fiber_switch:
	; save current context.
	; no need to store caller saved and ccr.
	mov.l	er7,	@(0x0c, er0) ; sp (contain return address)
	mov.l	er6,	@(0x08, er0) ; callee saved
	mov.l	er5,	@(0x04, er0) ; callee saved
	mov.l	er4,	@(0x00, er0) ; callee saved
	; load next context.
	mov.l	@(0x0c, er1), er7 ; sp
	mov.l	@(0x08, er1), er6 ; callee saved
	mov.l	@(0x04, er1), er5 ; callee saved
	mov.l	@(0x00, er1), er4 ; callee saved
	mov.l	er2,	er0	  ; subroutine return value / 1st arg.
	rts
これはx86
	.code32
FUNC (fiber_switch)
	//
	// save context. (from)
	movl	%esp, 0x10 (%eax)
	movl	%ebp, 0x0c (%eax)
	movl	%edi, 0x08 (%eax)
	movl	%esi, 0x04 (%eax)
	movl	%ebx, 0x00 (%eax)
	// load next context (to)
	movl	0x10 (%edx), %esp
	movl	0x0c (%edx), %ebp
	movl	0x08 (%edx), %edi
	movl	0x04 (%edx), %esi
	movl	0x00 (%edx), %ebx
	// subroutine return value/1st arg
	movl	%ecx,	%eax
	ret

データを見てみると受信がかなり不安定だし、受信データもバラつき大きいし (ノイズか?)今回のチャレンジは失敗に終わりそう。
#include <system.h>
#include <console.h>
#include <thread.h>
#include <fiber.h>
#include <delay.h>
#include <reg.h>
#include <string.h>

#include "local.h"
#include "timer.h"

STATIC uint8_t gps_tls[THREAD_STACK_SIZE (GPS_STACK_SIZE)]
__attribute ((aligned (4)));
STATIC void gps_thread (uint32_t);
STATIC int gps_thread_ready;

#define	FIFO_SIZE	64
#define	GPS_FIBER_STACK_SIZE	256

rbuf_nolock_t __recv_buf;
STATIC uint8_t buf_intr[RBUF_NOLOCK_SIZE (FIFO_SIZE)];
STATIC uint8_t buf_thread[FIFO_SIZE];
volatile size_t buf_thread_read_size;
uint8_t *buf_thread_read_p = buf_thread;

thread_t gps_th;

fiber_t fiber[3];
uint8_t fls[2][FIBER_STACK_SIZE (GPS_FIBER_STACK_SIZE)];
fiber_func gps_parser;
fiber_func gps_parser_2;

__BEGIN_DECLS
void uart3_intr (void);
uint8_t gps_getc (fiber_t);
__END_DECLS

GPS用のスレッドを作成します。
thread_t
gps_init ()
{
  thread_t th;

  th = thread_create (gps_tls, GPS_STACK_SIZE, "gps", gps_thread, 0);
  thread_start (th);

  while (!gps_thread_ready)
    thread_sleep (current_thread);

  return th;
}

void
gps_thread (uint32_t arg __attribute__((unused)))
{
  struct fiber myself;
  gps_th = current_thread;

  // PCLK 18.0000MHz 9615bps error 0.16%
  struct uart_clock_conf uart3 = { 0, 78, 1 | (2 << 4), CCLK4 };
  uart_init (UART3, &uart3, TRUE);
UART3の割り込みハンドラとスレッドコンテキストとのデータのやりとりに
リングバッファを用意します。
  // Interrupt context ring-buffer.
  __recv_buf = rbuf_nolock_init (buf_intr, FIFO_SIZE);

パースのためのファイバを用意します。fiber[0]はこのスレッド自分自身。
  // Create parser fiber.
  fiber[0] = fiber_init (&myself);
  fiber[1] = fiber_create (&myself, fls[0], GPS_FIBER_STACK_SIZE, gps_parser);
  fiber[2] = fiber_create (&myself, fls[1], GPS_FIBER_STACK_SIZE, gps_parser_2);
fiber_yieldした時の連結順番を設定します。
  fiber_twist (&myself, 2, fiber[1], fiber[2]);
ファイバをスタートします。
  fiber_start (&myself, fiber[1]);
  fiber_start (&myself, fiber[2]);

  // Now OK to log.
  gps_thread_ready = TRUE;
  thread_wakeup (shell_th);
  while (/*CONSTCOND*/1)
    {
      thread_sleep (current_thread);
受信割り込みから起こされるので、リングバッファにある内容をコピーします。
      cpu_status_t s = intr_suspend ();
      buf_thread_read_size = rbuf_nolock_read (__recv_buf, buf_thread, FIFO_SIZE);
      intr_resume (s);
今回読み込んだデータが処理し終わるまでファイバを廻します。
      buf_thread_read_p = buf_thread;
      while (buf_thread_read_size)
	{
	  fiber_yield (&myself, 0);
	}
    }
  // NOTREACHED
}

void
uart3_intr ()
{
  uint8_t c = *URBR (UART3_BASE);

  if (rbuf_nolock_write (__recv_buf, &c, 1) != 1)
    {
      iprintf ("UART3 overflow\n");
    }

割り込みが来たら内容をリングバッファに入れて、後処理スレッドを起こしま
す。ある程度遅延をかければその遅延の間に入った割り込みのデータをバッファ
に追加して後処理スレッドで一気に処理できます。
  timer_schedule_func ((void (*)(void *))thread_wakeup_once, gps_th, 100);
}


uint8_t gps_buf[64];

void FIBER_FUNC_ATTRIBUTE
gps_parser (fiber_t myself)
{
ここは親がfiber_startした時に処理されます。
  fiber_return_parent (myself, 0);
ここからは次のfiber_yieldから処理されます。
  // Next yield start here.
  uint8_t c;
  uint8_t *p = gps_buf;
  while (/*CONSTCOND*/1)
    {
      c = gps_getc (myself);
      *p++ = c;
      if ((c == ',') || (c == '\r') || (c == '\n'))
	{
	  *--p = '\0';
	  p = gps_buf;
区切り文字が来たら、そのバッファを次のファイバに送ります。
	  fiber_yield (myself, 0);
	}
    }
  // NOTREACHED
}

uint8_t
gps_getc (fiber_t self)
{
バッファがなくなれば、そのまま親に帰ります。また親にバッファがあればこ
こから初まるので、この関数の呼び出し先は連続してバッファが供給されるよ
うに見えます。
  if (buf_thread_read_size == 0)
    {
この場合、予定された次のファイバではなく、親に直接戻ってデータを待ちま
す。
      fiber_return_parent (self, 0);
    }
  assert (buf_thread_read_size);
  --buf_thread_read_size;

  return *buf_thread_read_p++;
}


void FIBER_FUNC_ATTRIBUTE
gps_parser_2 (fiber_t myself)
{
  bool target = FALSE;
  int gga_cnt = 0;
  fiber_return_parent (myself, 0);
  // Next yield start here.

  while (/*CONSTCOND*/1)
    {
このファイバにはgps_bufにトークンが用意された状態でまわってきます。
$GPGGAの、東経、北緯、受信状況、高度だけ表示してます。
      if (gps_buf[0] == '$')
	{
	  if ((target = strcmp ((const char *)gps_buf, "$GPGGA") == 0))
	    {
	      iprintf ("\n");
	      gga_cnt = 0;
	    }
	}
      if (target)
	{
	  if ((gga_cnt == 2)/*latitude*/ || (gga_cnt == 4) /*longitude*/||
	      (gga_cnt == 6)/*valid*/ || (gga_cnt == 9)/*altitude*/)
	    {
	      iprintf ("%s ", gps_buf);
	    }
	  gga_cnt++;
	}
これは親に戻ります。
      fiber_yield (myself, 0);
    }
  // NOTREACHED
}

/*
GPGSA
 GNSS DOP and Active Satellites

$GPGSA,A,3,23,20,11,13,,,,,,,,,07.3,04.2,05.9*0D

  Satellites in View
$GPGSV,3,1,09,17,83,311,00,20,42,046,42,04,39,294,26,23,38,099,41*76
$GPGSV,3,2,09,13,27,143,38,28,23,203,00,11,18,092,41,32,17,043,*76
$GPGSV,3,3,09,02,09,280,00*41

  Recommended Minimum Specific GNSS Data
$GPRMC,084249.695,A,3539.4058,N,13939.2779,E,0000.00,344.21,251009,,*3A

  Course Over Ground and Ground Speed
$GPVTG,344.21,T,,M,0000.00,N,0000.00,K*50

  Time & Date
$GPZDA,084250.695,25,10,2009,,*5A

  Global Positioning System Fix Data
$GPGGA,084250.694,3539.4058,N,13939.2779,E,1,04,04.2,00039.2,M,0039.4,M,000.0,0000*49

$GPGGA,
084250.694, UTC
3539.4058,N,
13939.2779,E,
1,		Quality (0: can't receive)
04,		# of Satellites
04.2,		HDOP
00039.2,M,	altitude
0039.4,M,	altitude WGS-84
000.0,		Data age
0000		DPGS ID
*49		checksum


*/






jiskan24から作ってみました。ラップタイムの部分は、ランタイムになんちゃっ てボールドにしてあります。一行目の反転表示が速度、横がラップ、ラップタ イムの下の棒グラフが30分までの経過時間の割合いを示しています。
LCDは通常のVRAM構成と違って、VRAMの一番最初に1B書き込むと、それは縦に8bit なので、フォントの変換とか面倒だ。
今回は使われていないイーサネット用のメモリ領域(0x7fe00000からの16KB)の 最初の1KBを仮想VRAMとしました。ここにイメージを作って、適時SPIで一気に LCDに転送です。イーサネットのメモリならDMA領域なので、SPIからSSPのSPIモー ドに変更すれば、DMA転送もできるだろうという目論見。
最近はCでも整合配列が使えるようになったのでちょっと楽になった。
イーサネットのメモリをVRAMにします。ページサイズで整合配列にしておきます。
uint8_t (*vram)[VRAM_PAGE_SIZE] = (uint8_t(*)[])ETHER_RAM_START;

フォントは24x24。1ページが8bitなので3ページを使います。
#define	BITMAP_WIDTH	24
#define	BITMAP_NPAGE	3
フォントはLCD用のフォーマットに変換しておきます。これは'0'
struct font
{
  uint8_t bitmap[BITMAP_NPAGE][BITMAP_WIDTH];
} fonts[] = {
//[0xcb]----------------------------------------------------------------------
  {{
    { 	0x00, //........
	0x00, //........
	0x00, //........
	0x00, //........
	0x00, //........
	0xc0, //||......
	0xf0, //||||....
	0x38, //..|||...
	0x0c, //....||..
	0x04, //.....|..
	0x02, //......|.
	0x02, //......|.
	0x02, //......|.
	0x02, //......|.
	0x04, //.....|..
	0x0c, //....||..
	0x38, //..|||...
	0xf0, //||||....
	0xc0, //||......
	0x00, //........
	0x00, //........
	0x00, //........
	0x00, //........
	0x00, //........
    },
    { 	0x00, //........
	0x00, //........
	0x00, //........
	0x00, //........
	0x00, //........
	0xff, //||||||||
	0xff, //||||||||
	0x00, //........
	0x00, //........
	0x00, //........
	0x00, //........
	0x00, //........
	0x00, //........
	0x00, //........
	0x00, //........
	0x00, //........
	0x00, //........
	0xff, //||||||||
	0xff, //||||||||
	0x00, //........
	0x00, //........
	0x00, //........
	0x00, //........
	0x00, //........
    },
    { 	0x00, //........
	0x00, //........
	0x00, //........
	0x00, //........
	0x00, //........
	0x00, //........
	0x03, //......||
	0x07, //.....|||
	0x0c, //....||..
	0x08, //....|...
	0x10, //...|....
	0x10, //...|....
	0x10, //...|....
	0x10, //...|....
	0x08, //....|...
	0x0c, //....||..
	0x07, //.....|||
	0x03, //......||
	0x00, //........
	0x00, //........
	0x00, //........
	0x00, //........
	0x00, //........
	0x00, //........
    },
  }},
...

仮想VRAMにはLCDに送るイメージそのものを。こうしたのは「'」や「.」を合成
したいため。このLCDモジュールはLCDへのデータの書き込みしかできないので
read-modify-writeができない。なので、こっちでやっておく。

// Write to V-RAM
void
lcd_vram_font (int font_index, int x, int y, bool inverse)
{
  int j, k;
  assert ((size_t)font_index < sizeof fonts / sizeof (fonts[0]));
  struct font *font = fonts + font_index;

  for (j = 0; j < BITMAP_NPAGE; j++)
    {
      uint8_t *p = vram[j + y] + x;
      uint8_t *bits = font->bitmap[j];
      for (k = 0; k < BITMAP_WIDTH; k++)
	{
	  uint8_t b = *bits++;
	  *p++ |= inverse ? ~b : b;
	}
    }
}

頃合を見て一気に転送。いずれ、ここはDMAにしたい。
// Transfer internal V-RAM data to LCD
void
lcd_vram_transfer ()
{
  int i, j;

  for (i = 0; i < VRAM_NPAGE; i++)
    {
      lcd_set_page (i);
      lcd_set_column (0);
      uint8_t *bits = vram[i];
      for (j = 0; j < VRAM_PAGE_SIZE; j++)
	lcd_data_putc (bits[j]);
    }
}


なんとなく接続したのをきっちりまとめないと。ここから車体に乗せるまでが 結構大変なんだよね。
GPSの書き込みフォーマットも考えておかないと。



まだトマトの収穫があります。今年は初夏からトマトを買うことがなかった。
収穫期には食べきれなくて、よくできたのは近所にお配りした程でした。


もう最近の収穫は小さいけれど、味は濃厚。酒のつまみになるくらい。
そして今日、ついに観念して歯医者に行った。日曜から歯が痛くてしかたなかっ たのだ。昨日は歯医者の前まで行ったものの、引き返してきてしまった。
しばらく通院です。気が重い。

ロガー続き。秋葉原の液晶工房 でFSTN液晶モジュール (128×64,SPI)[AD-12864-SPI] を買ってきました。今は20%オフなので、 1000円。しかし液晶画面は小さい。表示領域で27mmx13mm程度しかない。これじゃ いくらなんでも小さ過ぎだろうとも思いつつ、これにしたのは、これがSPI接続 だから。LPC2388はSPIモジュールがあるし、それだととても配線が少なくてす むし、プログラムもとても楽。
/usr/X11R7/lib/X11/fonts/misc/18x18ja.pcf.gzを変換してこのLCDに合うよう に16x16のフォントを生成してみたのだけど(pcf2bdfを改造して生成)、これは 走行中読めないだろう...。
フォントも作った方がいいかな。どうせ0123456789'.だけあればいいし。

SPIモジュールの設定は
void
spi_init ()
{
電源を入れて(デフォルトで電源は入っている)
  mcu_peripheral_power (PCONP_PCSPI, TRUE);
モジュールのクロックを設定。
  mcu_peripheral_clock (PCLK_SPI, CCLK8);	//72/8=9MHz
ピンをSPIに設定。
  mcu_pin_select (0, 15, 3); //SCK
  mcu_pin_select (0, 16, 3); //SSEL SPIマスタで使うならこれはなくてもいい。
  mcu_pin_select (0, 17, 3); //MISO データを受けとらないならこれもなくてもいい。
  mcu_pin_select (0, 18, 3); //MOSI
SPIのクロックを設定します。
  *S0SPCCR = 8;	// 9/8 = 1.125MHz
  //  *S0SPCCR = 0xff;	// 9/8 = 1.125MHz
マスターでMSBファーストです。
  *S0SPCR = CR_MSTR;	// Master. 8bit MSB
  //  *S0SPCR = CR_MSTR | CR_CPOL |CR_CPHA;	// Master. 8bit MSB
  //  *S0SPCR = CR_MSTR | CR_LSBF;	// Master. 8bit LSB
}

void
spi_fini ()
{

  mcu_peripheral_power (PCONP_PCSPI, FALSE);
}

void
spi_putc(uint8_t c)
{
  uint32_t r;

  *S0SPDR = c;
送信の完了を待ちます。
  while (!(*S0SPSR & SR_SPIF))
    ;
}

LCDモジュールはA0のHi/Lowでコマンドかデータかを区別するので、ここは
GPIOの1.30を接続しました。

#define	LCD_COMMAND()	GPIO_PIN_CLR (1, 30)
#define	LCD_DATA()	GPIO_PIN_SET (1, 30)
void
lcd_command_putc (int8_t c)
{
  LCD_COMMAND ();
  spi_putc (c);
  //  udelay(100);
}

void
lcd_data_putc (int8_t c)
{
  LCD_DATA ();
  spi_putc (c);
  //  udelay(100);
}

#define	LCD_RESET_PORT	3
#define	LCD_RESET_BIT	26
#define	LCD_A0_PORT	1
#define	LCD_A0_BIT	30
#define	LCD_LED_PORT	1
#define	LCD_LED_BIT	31
#define	LCD_COMMAND()	GPIO_PIN_CLR (1, 30)
#define	LCD_DATA()	GPIO_PIN_SET (1, 30)


struct pin_config pin_config[] =
  {
ハードウェアリセットはなくてもいいかもしれないけれど、一応できるように
しておきました。
    { LCD_RESET_PORT, LCD_RESET_BIT, 0, PIN_PULLUP, GPIO_OUTPUT },//GPIO #RESET
これはコマンドとデータの区別
    { LCD_A0_PORT, LCD_A0_BIT, 0, PIN_PULLUP, GPIO_OUTPUT },//GPIO A0
これはバックライトのLED。
    { LCD_LED_PORT, LCD_LED_BIT, 0, PIN_PULLUP, GPIO_OUTPUT },//GPIO LED_A
  };

ここまで継いでも、LPC2388はほとんどのピンに対してプルアップ、プルダウン
を設定できて、LEDを駆動できるので一切の外部回路不用。うれしい。
H8の場合、プルアップできるポート、LEDが駆動できるポートが限定されている
のでちょっと面倒だった。

void
lcd_display_init ()
{
  size_t i;
  struct pin_config *p = pin_config;

  spi_init ();

  for (i = 0; i < sizeof pin_config / sizeof (pin_config[0]); i++, p++)
    {
      mcu_pin_select (p->port, p->bit, p->func);
      mcu_pin_mode (p->port, p->bit, p->mode);
      if (p->dir != -1)
	gpio_dir (p->port, p->bit, p->dir);
    }
ハードウェアリセットします。
  udelay (200);
  GPIO_PIN_CLR (3, 26);
  udelay (200);
  GPIO_PIN_SET (3, 26);
バックライトLEDを点灯します。
  GPIO_PIN_SET (1, 31);
  udelay (200);

LCDモジュールをオンにして
  lcd_command_putc (DISPLAY_ON);
全ての電源を入れます。
  lcd_command_putc (POWER | POWER_BOOSTER | POWER_REGULATOR | POWER_FOLLOWER);
コントラストの調整。0でちょうどいい。サンプルコードの0x1fにしたりすると
まったく見えない。
  lcd_command_putc (BRIGHTNESS);
  lcd_command_putc (0 & BRIGHTNESS_MASK);
ここちょっと不明。サンプルのまま。
  lcd_command_putc (REGULATOR | (7 & REGULATOR_MASK));
  lcd_command_putc (LCD_BIAS_1_9);
これは液晶の上から下に0-63にします。デフォルトだと下から0-63。
  lcd_command_putc (SCAN_DIR_REVERSE);
これは液晶の左から右に0-127にします。ADC_RESERVEにすれば反対。
  lcd_command_putc (ADC_NORMAL);
V-RAMに関わらず全てのドットをONにするのをやめます。
  lcd_command_putc (ALL_POINT_OFF);
これはビットの反転設定。
  lcd_command_putc (DISPLAY_REVERSE);
液晶テストをオフにします。
  lcd_command_putc (STATIC_INDICATOR_ON);
  lcd_command_putc (INDICATOR_OFF);
V-RAMのスタート位置を設定します。
  lcd_command_putc (START_LINE | (0 & START_LINE_MASK));
  //  lcd_command_putc (READMODIFYWRITE_SET);
}

列の設定
void
lcd_set_column (int i)
{
  LCD_COMMAND ();
  spi_putc (COLUMN_ADDR_HI | ((i >> 4) & 0xf));
  spi_putc (COLUMN_ADDR_LO | (i & 0xf));
}

ページの設定(8行ごとに1ページ)
void
lcd_set_page (int i)
{
  LCD_COMMAND ();
  spi_putc (PAGE_ADDR | (i & PAGE_ADDR_MASK));
}
久々に狩りにでかけてきました。レベルシンクして、ジ・タでゴブペットを黒 5吟1でバーンパーティ。今はこういう戦略なのね。
LSにはまったく人がいなくなっていた。
元キャラは楽しかった思い出がつまらない思い出でかき消されるのが嫌でキャ ラデリしてしまったけれど、はやまったかな。もう一度空を散歩したい。


SH4A続き。

マイOSに戻ってOCBI命令ついてじっくり調べてみた。その結果、OCBI命令は
TLBの保護がリードオンリーの時は必ずTLB保護例外を出すことがわかった。そ
してOCBP,OCBWB命令はリードオンリーでも例外を出さない。

...これ、反対の挙動では?OCBIはキャッシュの無効化だけだから(メモリアクセ スがない) TLBの保護に関わらず成功し、OCBP,OCBWBは書き戻しがあるからTLB の保護を見るべきでしょう。
ということで、アドレス変換を有効にしたらOCBI命令は使えないことがわかっ た。メモリ割り付けのキャッシュからの操作だと、もしUビットが立っていたら (Dirty) V(有効)ビットを落としたところで自動的に書き戻しが発生するので、 無効化だけはできない。
一応その動作も確かめてみることにした。これは連想ビット(A)を立てて操作。
void
sh4a_dcache_wbinv_range_associate (vaddr_t va, vsize_t sz)
{
  vsize_t eva = ROUND_CACHELINE_32 (va + sz);
  va = TRUNC_CACHELINE_32 (va);

  _cpu_run_P2();
  while (va < eva)
    {
      uint32_t addr = SH4A_CCDA |
	(va & (CCDA_ENTRY_MASK << CCDA_ENTRY_SHIFT)) | CCDA_A;
      *(volatile uint32_t *)addr &= ~(CCDA_U | CCDA_V);
      va += SH4A_CACHE_LINESZ;
    }
  CPU_SYNC_RESOURCE ();
  _cpu_run_P1();
}
しかしだ。
wbinv_associate test buffer P1=890099e0 pattern=c0
[Entry cf](19e0)----------------------------------------------
way 0 tag=900b800 (D|V)
a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 
way 1 tag=9009800 (-|-)
dddddddd dddddddd dddddddd dddddddd dddddddd dddddddd dddddddd dddddddd 
way 2 tag=9009800 (-|-)
cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc 
way 3 tag=900b800 (-|-)
a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 
[Entry cf](19e0)----------------------------------------------
way 0 tag=900b800 (D|V)
a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 
way 1 tag=9009800 (D|V)
c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 
way 2 tag=9009800 (-|-)
cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc 
way 3 tag=900b800 (-|-)
a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 
Dump from P2
dddddddd dddddddd dddddddd dddddddd dddddddd dddddddd dddddddd dddddddd 
Dump from P2
dddddddd dddddddd dddddddd dddddddd dddddddd dddddddd dddddddd dddddddd 
[Entry cf](19e0)----------------------------------------------
way 0 tag=900b800 (-|-)本来はこのウェイがDirty|Validで残るはずなのに
a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 
way 1 tag=9009800 (D|V)ここがDirty|Validで残ってしまっている。
c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 
way 2 tag=9009800 (-|-)
cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc 
way 3 tag=900b800 (-|-)
a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 
test4> 
いろいろテストをしてみると、
wbinv_associate test buffer P1=890099e0 pattern=c0
[Entry cf](19e0)----------------------------------------------
way 0 tag=9009800 (-|-)
c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 
way 1 tag=9009800 (-|-)
aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa 
way 2 tag=9009800 (-|-)
c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 
way 3 tag=900b800 (D|V)
a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 
[Entry cf](19e0)----------------------------------------------
way 0 tag=9009800 (-|-)
c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 
way 1 tag=9009800 (D|V)
c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 
way 2 tag=9009800 (-|-)
c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 
way 3 tag=900b800 (D|V)
a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 
Dump from P2
c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 
Dump from P2
c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 
[Entry cf](19e0)----------------------------------------------
way 0 tag=9009800 (-|-)
c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 
way 1 tag=9009800 (-|-)
c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 
way 2 tag=9009800 (-|-)
c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0 
way 3 tag=900b800 (D|V) ウェイの最後のがDirty|Validなら無事。
a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 
test4> 
なんか、どうも連想じゃなく、ウェイで一番最初に見つかったのだけ操作して いるような...。メモリ割り付けの連想ビット(今迄使ってなかった)も、動作が おかしい。
アドレス変換を有効にしたら、キャッシュの無効化だけというのは無用なTLB例 外が起きてしまうためできない。メモリ割りつけのキャッシュからは無効化だ けはできない。ということで、 sh4a_dcache_inv_rangeではOCBIではなくOCBPにするしかないかな...。
そして連想ビットの動作がおかしいのは本当か?なにか勘違いしてるかも。そも そもメモリ割りつけのキャッシュの連想はTLBミスしたらなにもしないという仕 様なので、使いどころがよくわからない。



ロガー続き。3端子レギュレータは、表についているのは足を切って、裏から新
しくつけました。配線をまきこみながら両面テープで基板に固定。これなら
しばらくもつんじゃないかな。



秋月で買ってきたGPSレシーバモジュールキット(GPS-52)をつけてみました。 GPS用のバックアップ電源にCR2032用のホルダもつけました。これはGPSの衛星 の位置を記憶しておきたいため。GPS-52のピン番号は付属のCD-ROMの中のデー タシートにあった。

ピンがハーフピッチでどうしようか迷ったのだけど、基板に穴あけてピンに直 接ハンダ付け。なんかまた走行中の振動で外れそうな感じ。早く動かしてみた かったんだ。
秋月のキットにはRS232Cレベルに変換するチップも同梱だけれど、それは使わ ずに直接LPC2388のUART3に直結。

こんなプログラムで。
uint32_t
test (int32_t argc __attribute__((unused)),
     const char *argv[] __attribute__((unused)))
{
  struct uart_clock_conf uart3 = { 0, 78, 1 | (2 << 4), CCLK4 };
  uart_init (UART3, &uart3);

  while (/*CONSTCOND*/1)
    iputc (uart_getc (UART3), SERIAL);
  //NOTREACHED
  return 0;
}
いざ電源を入れてテストしてみると...
$GPGSA,A,1,,,,,,,,,,,,,99.9,99.9,99.9*09
$GPRMC,000756.970,V,3600.0000,N,13600.0000,E,9999.99,999.99,251009,,*29
$GPVTG,999.99,T,,M,9999.99,N,9999.99,K*59
$GPZDA,000757.970,25,10,2009,,*50
$GPGGA,000757.970,3600.0000,N,13600.0000,E,0,00,99.9,00000.0,M,0000.0,M,000.0,0000*40
$GPGSA,A,1,,,,,,,,,,,,,99.9,99.9,99.9*09
$GPRMC,000757.970,V,3600.0000,N,13600.0000,E,9999.99,999.99,251009,,*28
$GPVTG,999.99,T,,M,9999.99,N,9999.99,K*59
$GPZDA,000758.970,25,10,2009,,*5F
$GPGGA,000758.970,3600.0000,N,13600.0000,E,0,00,99.9,00000.0,M,0000.0,M,000.0,0000*4F
これはNMEA-183フォーマット。$GPGSA,A,1の部分が受信不能を示している。こ の基板とノートをつなげて庭をうろうろしてみるも、変わらず。
やっぱり50x50mm以上のGND層を作らなかったのがいけなかったのかな...
と暗澹たる気持ちでそのまましばらく机の上に放置していたら(ここで窓を開け ていたのがよかった)、
$GPGSA,A,1,,,,,,,,,,,,,99.9,99.9,99.9*09
$GPGSV,3,1,12,27,56,000,00,23,50,000,00,28,47,000,00,17,23,000,37*70
$GPGSV,3,2,12,13,22,000,33,04,18,000,00,15,10,000,00,08,10,000,00*78
$GPGSV,3,3,12,21,08,000,00,03,08,000,00,10,08,000,,19,04,000,00*7F
$GPRMC,081335.791,V,3600.0000,N,13600.0000,E,9999.99,999.99,251009,,*20
$GPVTG,999.99,T,,M,9999.99,N,9999.99,K*59
$GPZDA,081336.791,25,10,2009,,*5B
$GPGGA,081336.791,3600.0000,N,13600.0000,E,0,00,99.9,00000.0,M,0000.0,M,000.0,0000*4B
受信はできていないけれど、
$GPGSV,3,1,12,27,56,000,00,23,50,000,00,28,47,000,00,17,23,000,37*70
$GPGSV,3,2,12,13,22,000,33,04,18,000,00,15,10,000,00,08,10,000,00*78
$GPGSV,3,3,12,21,08,000,00,03,08,000,00,10,08,000,,19,04,000,00*7F
の情報が増えた。これは衛星の位置情報だ。気をよくして窓際に置いておくと...

受信できた!
0,0000*40
$GPGSA,A,3,23,20,11,13,,,,,,,,,07.3,04.2,05.9*0D
$GPGSV,3,1,09,17,83,311,00,20,42,046,42,04,39,294,26,23,38,099,41*76
$GPGSV,3,2,09,13,27,143,38,28,23,203,00,11,18,092,41,32,17,043,*76
$GPGSV,3,3,09,02,09,280,00*41
$GPRMC,084249.695,A,3539.4058,N,13939.2779,E,0000.00,344.21,251009,,*3A
$GPVTG,344.21,T,,M,0000.00,N,0000.00,K*50
$GPZDA,084250.695,25,10,2009,,*5A
$GPGGA,084250.694,3539.4058,N,13939.2779,E,1,04,04.2,00039.2,M,0039.4,M,000.0,0000*49
$GPGSA,A,3,23,20,11,13,,,,,,,,,07.3,04.2,05.9*0D
$GPGSV,3,1,09,17,83,311,00,20,42,046,42,04,39,294,00,23,38,099,41*72
$GPGSV,3,2,09,13,27,143,38,28,23,203,00,11,18,092,41,32,17,043,*76
$GPGSV,3,3,09,02,09,280,00*41
$GPRMC,084250.694,A,3539.4058,N,13939.2779,E,0000.00,344.21,251009,,*33
$GPVTG,344.21,T,,M,0000.00,N,0000.00,K*50
$GPZDA,084251.694,25,10,2009,,*5A
$GPGGA,084251.694,3539.4058,N,13939.2779,E,1,04,04.2,00039.2,M,0039.4,M,000.0,0000*48
GPSって衛星探すのに結構手間がかかるのね。部屋で窓を閉めてしまうと受信不 能。これはGND層を作らなかったのもあるかも。

このGPSモジュール、更新間隔が1秒。1秒は長い。これじゃ統計的なラインしか わからないな...。緯度経度の精度は10^-4分、一秒が30mくらいなので 30*60/10000*100で18cmくらい。本当???


菜園状況。さっそく堆肥をすきこんで、キャベツを定植しました。10株をメイ
ンの畝に。残りの5株は軒先に。どれだけ生き残るか...



大根も小さいうちにヨトウムシにやられなければ、かなり安泰。追加で種まい たのはまた、ヨトウムシにやられている。土をほじくり返して3匹ほど殺した。 本当ヨトウムシは大変だ。昼間は土に潜ってるから如何ともし難い。
大根の葉っぱにはモンシロチョウの幼虫が数匹。これも殺害。
大根はもっと植えておけばよかったな。本当なら間引きする範囲なのだけど、 生き残ったのが3本だから抜くに抜けない。やっぱり夏〜秋に植えるのは害虫が 厳しい。まだ春先の方が楽だ。

一応寒冷紗をかけておきました。最後まで実がなっていたトマトを抜いて(もう この時期になるといつまでたっても実が大きくならない)、ネギの種を蒔いたけ れど、もう遅いかな。

SH4A続き。OCBI命令が謎にデータTLB保護違反例外を出す。OCBPは大丈夫なのだ けれど。これはマイOSに戻って検証したい。
pmapのキャッシュまわり、実行時に、それがI/D分離キャッシュか、ライトバッ クキャッシュか、エイリアス問題があるか、ダイレクトマップ(1-way)かどうか をその場で分岐するのは、無駄だ。
ということで、pmap_initの時点でキャッシュ操作を特定してしまうようにした。 pmapで必要なのは、
(1) もうこのページはいらない
(2) まだいるけれど、一時的にマップから外す。
(3) 他のエリア(P1/P2)からアクセスするためにキャッシュの整合性をとりたい。
(4) 書きこんだ所から実行するから、それが命令キャッシュにきちんと入るよ

ここで
I/D統合キャッシュでライトスルーでエイリアス問題がなければ、一切の
キャッシュ操作の必要がない。

I/D統合キャッシュでライトバックでエイリアス問題がなければ、(2)と(3)がいる。

I/D統合キャッシュでライトバックでエイリアス問題があれば、(1)と(2)と(3)がいる。

I/D分離キャッシュでライトスルーでエイリアス問題がなければ、(4)がいる。

I/D分離キャッシュでライトバックでエイリアス問題がなければ、(2)(3)(4)がいる。

I/D分離キャッシュでライトバックでエイリアス問題があれば、(1)(2)(3)(4)がいる。

さらにダイレクトマップではない場合(2-way以上のセットアソシアティブ)、
インデックス操作の無効化の際には必ず、書き戻しを先にしないといけない。
これを最初に設定してしまい、pmapの中ではI/D分離キャッシュでライトバック でエイリアス問題があるとして操作。実際にはキャッシュの状況によってそれ はNOPになるという方向にしてみた。
ただ、エイリアス問題は、エイリアスの解決方によってvm_pageに登録される pmap:vaの組が変わり、それによってキャッシュ操作のポリシーも変わるので考 えるところがある。
そしてさらに挑戦的にキャッシュ操作時のASIDを調べて、キャッシュ操作の対 象のpmapがASIDであれば、カーネルマップ以外でもヒット操作にしてみるよう にしてみた。これはn-wayセットアソシアティブの時だけにすべき。ダイレクト マップなら、インデックス操作もヒット操作も変わらないから。その場合無駄に TLB例外が発生することになる。
ここでOCBI命令がおかしいことに気付いた。とりあえずOCBIをOCBPに置きかえ れば動いてはいる。



筑波選手権のランキングが出ていた。なんとまた19位!。うーん。やはり神は俺
を19に。筑波選手権のS80クラスで俺ほど19の似合う人間はいないからな。(
MCFAJだと他に2人くらいいた)

さてロガーを外して調べてみると、やはり3端子レギュレータの足にクラックが 入っていた。手で押さえたりすると電源がON/OFFする。うーん。


SH4A続き。頭からキャッシュ落ちしない程度に触れておこう。pmap_kenter_paと pmap_kremoveについて。
void
pmap_kenter_pa(vaddr_t va, paddr_t pa, vm_prot_t prot)
{
	pt_entry_t *pte, entry;

仮想アドレスはページアラインであることが前提。
	KDASSERT((va & PGOFSET) == 0);
そしてその仮想アドレスはP3のみ。カーネル空間ではP0(0-0x80000000)も
使えるけれど、UVMからは使わない設定にしている。
	KDASSERT(va >= VM_MIN_KERNEL_ADDRESS && va < VM_MAX_KERNEL_ADDRESS);

カーネルのマップはASIDがどう設定されていても参照できるようにPG_SH(共有)
の設定。こう設定できるのは、ユーザ空間を0-0x8000000のU0、カーネル空間で
TLBを参照するのは0xc0000000-0xe0000000のP3と分けているから。P0も使った
場合には共有ページにしてしまうとTLB多重ヒット例外でリセットしてしまう可
能性がある。

もしP0まで使うならカーネル専用のASID(0)に設定する必要があり、さらにその
P0のページは共有設定してはいけない。

そう考えると、先日実装した32bit物理アドレス拡張モード用のpmap_zero_page,
pmap_copy_pageの実装は間違えていて、PG_SHはなしにして、memcpy,memsetの前に
ASIDを0(カーネル)に設定して、その後で元のASIDに戻さないといけない。
そうしないと、もしU0で0-0x2000をマップしていたら、TLB多重ヒット例外だ。

	entry = (pa & PG_PPN) | PG_V | PG_SH | PG_4K;

これは書き込みするかしないかの設定。PG_Dを設定することで、初期ページ書
き込み例外を起こさせないようにしている。

	if (prot & VM_PROT_WRITE)
		entry |= (PG_PR_KRW | PG_D);
	else
		entry |= PG_PR_KRO;

struct vm_pageが設定されていれば(メモリ)キャッシュを有効に。設定して
いなければ(メモリマップドのデバイス)、アンキャッシュド。
	if (PHYS_TO_VM_PAGE(pa))
		entry |= PG_C;


この仮想アドレスに対するページテーブルエントリを__pmap_kernelから取得し
ます。__pmap_kernelはP3エリアを担当する。ここで失敗しないのはUVMが
先だってpmap_growkernelでそのエリアまでページテーブルエントリスロット
を確保しているから。
#define	PMAP_GROWKERNELをデファインしないのはpmap_initにおいて
pmap_growkernel(VM_MAX_KERNEL_ADDRESS);
をしておくのと同じ(全部のページテーブルページスロットを確保しておく)。
	pte = __pmap_kpte_lookup(va);
	KDASSERT(*pte == 0);
PTEを登録。
	*pte = entry;
そしてこれをTLBに乗せておきます。
	sh_tlb_update(0, va, entry);

pmap_kenter_paでは、__pmap_pv_enter()を呼ばない。つまりMD拡張の物理アド
レス-仮想アドレスのマッピングの組を登録しない。これはpmap_kenter_paのマッ
プはpmap_kernel()に固定されているからだ。pmap_enterでは一つの物理アドレ
スのマッピングにいくつかのアドレス空間+そこでの仮想アドレスの組が存在し、
それがキャッシュのエイリアス問題を引きおこすので、それを検出するために
登録する。pmap_kernel()ではそのようなことはないので__pmap_pv_enter()す
る必要がない。

}

void
pmap_kremove(vaddr_t va, vsize_t len)
{
	pt_entry_t *pte;
	vaddr_t eva = va + len;

ここではpmap_kenter_paで登録されたページを削除します。
これらのアサートはpmap_kenter_paと同じく、これらがページサイズのアラインで
あることと、P3にマップされていることを確認。
	KDASSERT((va & PGOFSET) == 0);
	KDASSERT((len & PGOFSET) == 0);
	KDASSERT(va >= VM_MIN_KERNEL_ADDRESS && eva <= VM_MAX_KERNEL_ADDRESS);

	for (; va < eva; va += PAGE_SIZE) {
		pte = __pmap_kpte_lookup(va);
ここでpteがNULLということは既にこの仮想アドレスの領域に対するページテー
ブルページスロットが削除されてしまったということだ。それは早すぎる。
		KDASSERT(pte != NULL);
もし既に穴喰いで削除されていたとしたらスキップ。だけれど、こういう機会は
あるのだろうか。
		if (*pte == 0)
			continue;
このページを削除するにあたってキャッシュに対してすることは、
 ライトバックキャッシュであれば、書き戻しておく。

無効化に関して

エイリアスがない場合でも次にこの物理ページを使う時にキャッシュに残って
いれば、それが読めてしまう。それは保護の問題でよくないので無効化したい。

エイリアスがある場合、無効化しなければ、次にこの物理ページを使った場合、
それがpmap_kernel()でなければ、他のインデックスにこの物理ページのキャッ
シュが乗って、エイリアスになる。うっかりそこでキャッシュフラッシュすれ
ばこの古いキャッシュが書き戻されることになってしまうかもしれない。

ということで、ここの方針は常に書き戻し無効化。なのでここの実装はちょっ
と疑問があるけれど、UVMがユーザ領域のマッピングの際にゼロページしていれ
ばこれでいい。(たぶんそうしてるはず...)

PHYS_TO_VM_PAGE(*pte & PG_PPN)はこれがメモリへのマッピングであることを
調べている。(メモリでなければアンキャシュドマッピングだ)
		if (SH_HAS_VIRTUAL_ALIAS && PHYS_TO_VM_PAGE(*pte & PG_PPN))
			sh_dcache_wbinv_range(va, PAGE_SIZE);
		*pte = 0;

そしてこのアドレスに対するTLBのエントリも無効化します。
		sh_tlb_invalidate_addr(0, va);
	}
}



絶好のコンディションのBS走行会でした。岡山の後の筑波だと近過ぎてうれし
い。定速走行のトラックの後で、ぼっーっと運転して、お気楽さを満喫。長距
離で呑気に走ってるといつまでたっても辿りつかないから、ある程度がんばっ
て踏んでいかないといけないのが疲れるんだよね。


例によって1ヘア裏。やっぱり筑波のここにいるのが落ちつく。レースの時も朝 一度ここにチャリンコでやって来て気分を落ちつけています。
走行準備していると、なんとアンダーカウル家に忘れてきた!! 04のカウルだと 最低地上高が低くてタラップで亀の子になってしまうので、別体で積んでるん だよね。おまけにフロントスタンドも忘れた。ウォーマーもかけれない。
BS走行会でよかった。
暖機してみると、タコメータが壊れはじめた。たまにピコピコ針が落ちてしま う。TT
8:00の一本目はピストンのナラシ。ロガーはというと、この時間ならまぁなん とか目を凝らせば読めなくもないレベル。更新されるラップタイプに、にんま りしながら周回してると、ロガーがリセットした。
ピットインして調べてみると、電源系がまたいかれた。また三端子レギュレー タの足が折れたのかも。
ピストンはちゃんとできてました。これはレース用にとっておいて、中古ピストン に交換してリングは新品に。
一本目最後3周くらいしてみて、どうも引っ張ると息つきするような感じなので MJを一段上げるかと思ってMJ170を探すもなかったので(RS125のPJ38に入れたま まだ)この密度高度十分MJ168のままでいける範囲だ。しかし....

二本目は10:00。走りはじめてみるとやっぱりE/Gのフィールが全然いつもと違 う。いつものように引っぱるとパスパスしてしまう。早め早めにシフトアップ するとやたら調子いい。すんごいトルク感あるし、いつもなら150m先で6速なの に200mで6速入ってしまう。そして6速入れても失速感がない。15:34じゃショー トで最終の入口でひっぱり切れないくらいだ。
1ヘアの立ちあがりもモリモリ前に進んでフロントがチャタる。
もしかしてアンダーカウルなしで走行してチャンバーが冷えたからかな? と思っ ているけどどうだろう。
でもタコメータも信用できないし、ロガーは壊れてしまったし、P-LAPは外して 来てしまったしで、よくわからず。
10:00の走行になると、LEDはまったく見えない。やっぱりこれはだめだったな〜。

久々にもつ定食べて、D'sでオイルとプラグ買って、ホームジョイ本田によって 牛糞発酵堆肥40lを二袋買って帰宅。
7:55 -10m 14.6℃ 68.8% 1017.1hPa 晴れ
ピストンナラシ

9:55 206m 20.2℃ 59.0% 1016.7hPa 晴れ
12巻き 残スト7/12


ロガー続き。P-LAPセンサにロガーから5V供給して、ストロベリーリナックスの
7セグメントLED表示キットをLPC2388のUART3に接続。マシンに取り付け。なん
とか間にあった。時間がなくてかなりなげやり。本当は今迄P-LAPがついていた
場所に取り付けようと思っていたのだけど、ケーブルの長さが足りなくて。一
番長くとれそうなフロッピーディスクドライブのケーブルから作ったんだけど
ね...。仕方なくタコメータの上に両面テープで貼りつけ。



しかし、これが日中の走行中に視認できるだろうか。そこが一番の問題だ。 一応、スモークのエンビ板を被せてあるけれど。どうかな〜。
時間なしの即興の工作も、気軽でなかなか楽しいもの。



CR85の整備。新品ピストン(+ピン+ベアリング)入れて、リングは中古。一本目これでピストン作って、二本目は中古ピストンに新品リングで。



SH4A続き。おやすみということにしていたけれど、動かないのがどうも気になっ てしかたがないのでUVMいじって強引に動かしました。32bit拡張モードで 512MB全部UVMに乗せました。これで胸のつかえがおりた...。
Copyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
    2006, 2007, 2008, 2009
    The NetBSD Foundation, Inc.  All rights reserved.
Copyright (c) 1982, 1986, 1989, 1991, 1993
    The Regents of the University of California.  All rights reserved.

NetBSD 5.99.20 (RENESAS7785LCR) #700: Tue Oct 20 20:57:14 JST 2009
        uch@alexandrite:/usr/src/sys/arch/evbsh3/compile/RENESAS7785LCR
general exception handler:      324 byte
TLB miss exception handler:     272 byte
interrupt exception handler:    288 byte
total memory = 511 MB
avail memory = 498 MB
Renesas evaluation board R0P7785LC0011RL
mainbus0 (root)
cpu0 at mainbus0: SH4A 599.999 MHz PCLOCK 49.999 MHz
cpu0: 32KB/32B 4-way set-associative Instruction cache.
cpu0: 32KB/32B 4-way set-associative Data cache.
cpu0: U0, P0, P3 write-back; P1 write-back
cpu0: full-associative 4 ITLB, 64 UTLB entries
cpu0: multiple virtual storage mode, SQ access: kernel, wired 7
cpu0: 32bit physical address mode. TLB-compatible mode.
VPN:80000000 (V) PPN:0 (V) 512MB, bufferd write, cacheable, write-back,
VPN:a0000000 (V) PPN:0 (V) 512MB, bufferd write, uncacheable, write-back,
shb0 at mainbus0
scif0 at shb0
scif0: console
rn_init: radix functions require max_keylen be set
root on md0a dumps on md0b
mountroot: trying ffs...
root file system type: ffs
WARNING: no TOD clock present
WARNING: using filesystem time
WARNING: CHECK AND RESET THE DATE!
init: copying out flags `-s' 3
init: copying out path `/sbin/init' 11
erase ^?, werase ^W, kill ^U, intr ^C

mount_kernfs: kernfs on /kern: Operation not supported by device
# 
# df
Filesystem   1K-blocks       Used      Avail %Cap Mounted on
/dev/md0a          3811       1323       2488  34% /
# 

uvm_km.cの中の
	uvm_km_alloc()
	uvm_km_alloc_poolpage_cache()
	uvm_km_alloc_poolpage()
uvm_map.cの中の
	uvm_kmapent_alloc()

のuvm_pageallocをuvm_pagealloc_stratにしてP1からアクセスできるエリアに
してやればPMAP_MAP_POOLPAGEにやってくる物理アドレスはP1だけになる。ブー
トして一番最初にPMAP_MAP_POOLPAGEを使うことになるのは
uvm_kmapent_alloc()@uvm_map.cでアロケートされた部分だ。

これをやってやれば、P1からアクセスすることのできないエリアまでUVMに積んだ
としても

#define	PMAP_MAP_POOLPAGE(pa)		SH3_PHYS_TO_P1SEG((pa))

として動く。そして512MB積むとinitでセグメンテーションフォルトになってしまうの
は、新しく書いたpmap_copy_pageのバグだった。昨日のソースから。

		sh_tlb_update(0, src_va,
		    (src & PG_PPN) | PG_PR_KRW | PG_V | PG_D | PG_4K | PG_SH);
		sh_tlb_update(0, dst_va,
		    (src & PG_PPN) | PG_PR_KRW | PG_V | PG_D | PG_4K | PG_SH);
                    ^^^^^ここはdst。これじゃコピーされてないよ!


32bit拡張モード用のpmap_copy_pageについて。

まずは物理アドレスを指定された時にそこにマップしている仮想アドレスの
キャッシュをフラッシュするルーチンを書きました。

pmapの中でのキャッシュの扱いについて。

SHは仮想アドレスインデックス物理アドレスタグキャッシュ。それは仮想アド
レスでキャッシュのインデックスを生成して、そのインデックスに目当ての
キャッシュがあるかどうかは物理アドレスで特定する。

なのでタグの生成にはMMUの助けがいる。MMUはASID(アドレススペース
ID)と仮想アドレスの組からマッチして物理アドレスを返す。OS側でもASIDは
struct pmapの形でpmap+vaの組でこれを表現している。

キャッシュ操作にはICBI,OCBP,OCBI,OCBWB命令と、メモリ割り付けのキャッ
シュアレイの操作がある。キャッシュ命令の場合はその仮想アドレスから物理
アドレスの変換にMMUを使う。ヒットしているかどうかは物理アドレスで同定
するからだ。なので、命令の発行時に目的のアドレススペースになるように
MMUにASIDを設定し、OS側もその例外をハンドルできるようにしておく必要
がある。つまりpmap_activateする必要がある。

メモリ割りつけの場合はMMUの介在がないのでTLBミスも存在しないし、アドレ
ススペースを切り替える必要もない。ただ、ヒットするかどうかはわからない
のでその仮想アドレスから計算されるインデックスの全てのウェイについて操
作しないといけない。ちょっとメモリアクセスが増える。

メモリ割りつけのキャッシュからインデックス操作をする場合、皆殺しなので
悪影響がないようにしないといけない。例えば他のウェイにまだ書き戻されて
ないデータがキャッシュが乗っているのに、これに対して書き戻しをせずに無
効化してしまったらデータをロスしてしまう。(ライトバックの場合)

なので、ライトバックキャッシュの場合インデックス操作では無効化だけとい
うのはしてはいけない。ライトスルーならOK。

もし、該当する仮想アドレスがカーネル空間のものなら、アドレス空間の変更
なしに、ICBI,OCBP,OCBI,OCBWB命令を発行していい。これはページテーブルペー
ジとか、pmap_kenter_paで登録されたkernel_mapのページ。

  + ICBI,OCBP,OCBI,OCBWBを発行するならアドレス空間を考慮する。
  + pmapの関数はカーネル空間。
  + アドレス空間を変更せずにユーザ空間のキャッシュをいじるにはインデッ
  クス操作しかない
  + その際、他のウェイに気をつけろ。

ということだ。実際のところNetBSD/sh3ではインデックス操作には書き戻し無
効化しか用意していない。

enum cache_ops {
	CACHE_INV = 0,
	CACHE_WB = 1,
	CACHE_WBINV = 2,
};

// cache ops for vm-mapped pages.
void
__pmap_dcache_pa(paddr_t pa, enum cache_ops ops)
{
全部同じ関数だけれど、操作側でしたいことを明確にしたくてこうした。
できればsh_cache_ops._dcache_wb_range_indexは追加してもいいだろう。
これはその仮想アドレスがユーザ空間でもうまくいくようにしてある。
カーネル空間専用なら、インデックス操作は必要ない。
	void (*cache_func[])(vaddr_t, vsize_t) = {
		sh_cache_ops._dcache_wbinv_range_index,
		sh_cache_ops._dcache_wbinv_range_index,
		sh_cache_ops._dcache_wbinv_range_index
	};

	struct vm_page *pg;
	struct vm_page_md *pvh;
	struct pv_entry *pv;

もしこの物理アドレスに仮想アドレスがまだマップされてないならなにもする
ことはない。(コピーページの時はないかも?よく調べてない。 ゼロページの時
だとUVMはゼロページしてからマップするのでよくある)

	if ((pg = PHYS_TO_VM_PAGE(pa)) == NULL)
		return;
	pvh = &pg->mdpage;
この物理ページにマップされている仮想アドレスでループ。
	SLIST_FOREACH(pv, &pvh->pvh_head, pv_link) {
アドレス空間を変更せずにインデックス操作でフラッシュ。この他、
pmap_activate(pv->pv_pmap)してキャッシュヒット命令にして、またカーネル
マップに戻すのもありかも?(まだやってない)
	    cache_func[ops](pv->pv_va, PAGE_SIZE);
//	    printf("%s: cache flush pmap=%p va=%lx\n", __FUNCTION__,
//		pv->pv_pmap, pv->pv_va);
	}
}

void
pmap_copy_page(paddr_t src, paddr_t dst)
{
#ifdef SH4A_EXT_ADDR32
物理アドレスがP1/P2からマップされる空間ならP2から処理します。
	if (src < 0x20000000 && dst < 0x20000000)
		goto maped_area;
	struct vm_page *pg_src, *pg_dst;
	vaddr_t dst_va;
	vaddr_t src_va;
	int s = _cpu_intr_suspend();

実際のコピーを今現在の仮想アドレスからではなく、別のマップから行うので
キャッシュの整合性をとります。

コピー元が仮想アドレスでマップされているなら、まず書き戻します。
(write-thruならいらない)
	// If mapped to virtual address, sync cache.
	if ((pg_src = PHYS_TO_VM_PAGE(src)) != NULL)
		__pmap_dcache_pa(src, CACHE_WB);
コピー先が仮想アドレスでマップされているなら、落とします。(実際はインデックス
操作なので書き戻し無効化。)
	if ((pg_dst = PHYS_TO_VM_PAGE(dst)) != NULL)
		__pmap_dcache_pa(dst, CACHE_INV);

ここでコピー元、コピー先の物理アドレスを仮のP0空間のアドレスにとり、ここで
コピー。このページはアンキャッシュドでマップしているのでキャッシュの考慮は
無用。
	src_va = 0;
	dst_va = PAGE_SIZE;
	sh_tlb_update(0, src_va,
	    (src & PG_PPN) | PG_PR_KRW | PG_V | PG_D | PG_4K | PG_SH);
	sh_tlb_update(0, dst_va,
	    (dst & PG_PPN) | PG_PR_KRW | PG_V | PG_D | PG_4K | PG_SH);
P0空間でコピー。上のsh_tlb_updateで両方ともTLBに乗せてあるので例外は起きない。
TLB落ちさせないために割り込みも押さえてある。
	memcpy((void *)dst_va, (void *)src_va, PAGE_SIZE);
この仮のマップを落とします。
	sh_tlb_invalidate_addr (0, src_va);
	sh_tlb_invalidate_addr (0, dst_va);
	_cpu_intr_resume (s);
	return;
maped_area:
#endif
これは今迄通りP1/P2からアクセスできるエリア。
	if (SH_HAS_VIRTUAL_ALIAS) {	/* don't polute cache */
SH_HAS_VIRTUAL_ALIASで分けているのは、キャッシュインデックスがアドレス
空間によって変わるから。同じ物理アドレスが別のキャッシュインデックスに
乗っていることもある。ので、この物理ページにマップされる仮想アドレスに
対して全て操作しないといけない。
		/* sync cache since we access via P2. */
ここでも仮想アドレスにマップされていた時のキャッシュの整合性をとります。
		__pmap_dcache_pa(dst, CACHE_INV);
		__pmap_dcache_pa(src, CACHE_WB);
		memcpy((void *)SH3_PHYS_TO_P2SEG(dst),
		    (void *)SH3_PHYS_TO_P2SEG(src), PAGE_SIZE);
	} else {
実質物理キャッシュの場合、完全に物理アドレスでタグづけされるのでキャッシュド
のP1から操作すればいい。(どのアドレス空間からの仮想アドレスでも同じ物理アドレス
には同じインデックスになる。)
		memcpy((void *)SH3_PHYS_TO_P1SEG(dst),
		    (void *)SH3_PHYS_TO_P1SEG(src), PAGE_SIZE);
	}
}



SH4A続き。そうもSH4Aばかりいじってもいられないのでちょっとここでおやす
み。割り込みがあがっているように見えたのはscifintrで無限ループに入って
いた。SH4(775x)のSCIFはFIFOのカウンタがRX,TX一つのレジスタだったのが、
SH4A(778x) では別々のレジスタになっていたのだ。これでなんとかSH4互換モード
では動くようになった。

ちょっと以下俺コンテキストセーブ。内容はあってないかも。ちょっとロガー なんとかしないとBSに間に合わない。
Copyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
    2006, 2007, 2008, 2009
    The NetBSD Foundation, Inc.  All rights reserved.
Copyright (c) 1982, 1986, 1989, 1991, 1993
    The Regents of the University of California.  All rights reserved.

NetBSD 5.99.20 (RENESAS7785LCR) #643: Mon Oct 19 20:46:10 JST 2009
        uch@alexandrite:/usr/src/sys/arch/evbsh3/compile/RENESAS7785LCR
general exception handler:      324 byte
TLB miss exception handler:     272 byte
interrupt exception handler:    288 byte
total memory = 127 MB
avail memory = 120 MB
Renesas evaluation board R0P7785LC0011RL
mainbus0 (root)
cpu0 at mainbus0: SH4A 599.999 MHz PCLOCK 49.999 MHz
cpu0: 32KB/32B 4-way set-associative Instruction cache.
cpu0: 32KB/32B 4-way set-associative Data cache.
cpu0: U0, P0, P3 write-back; P1 write-back
cpu0: full-associative 4 ITLB, 64 UTLB entries
cpu0: multiple virtual storage mode, SQ access: kernel, wired 7
cpu0: 29bit physical address mode. TLB-compatible mode.
shb0 at mainbus0
scif0 at shb0
scif0: console
rn_init: radix functions require max_keylen be set
root on md0a dumps on md0b
mountroot: trying ffs...
root file system type: ffs
WARNING: no TOD clock present
WARNING: using filesystem time
WARNING: CHECK AND RESET THE DATE!
init: copying out flags `-s' 3
init: copying out path `/sbin/init' 11
erase ^?, werase ^W, kill ^U, intr ^C

mount_kernfs: kernfs on /kern: Operation not supported by device
# df
Filesystem   1K-blocks       Used      Avail %Cap Mounted on
/dev/md0a          3811       1323       2488  34% /
# ls
.profile     etc          kern         mnt2         tmp          usr.install
bin          install      libexec      sbin         upgrade      var
dev          install.sub  mnt          targetroot   usr
# 


さて、ここで物理アドレス32bitモードにして512MB全部UVMに積みたい。全ての
アドレスがP1/P2から参照できなくなるので

SH3_PHYS_TO_P1SEG((pa))
SH3_P1SEG_TO_PHYS((va))

のマクロを使ったところをなんとかしないといけない。512MBまでならPMBを使っ
てP1/P2全てにメモリをマップしてしまい、その領域のデバイスメモリはP3から
マップしてやればなんとかなりそうだけれど。

ここでSH4互換モードのまま
#define	PMAP_MAP_POOLPAGE(pa)		SH3_PHYS_TO_P1SEG((pa))
#define	PMAP_UNMAP_POOLPAGE(va)		SH3_P1SEG_TO_PHYS((va))

このデファインをなくしてみた。こうなるとUVMの方で、適当にとってきたペー
ジをpmap_kenter_paでマップする。pmap_kenter_paはpmap_kernel()だけのマッ
ピングをひき受けるルーチンで、これは'wired'。このwiredはTLBのワイヤード
ではなく、仮想アドレス-物理アドレスの組が変更されることがないことを意味
する。pmap_kenter_paでマッピングされているページはシェルが立ちあがった
以降で300ページ弱ある。

つまりpool(9)がP1からP3に移る(独自のアロケータを設定してpool_initしなければ)。

しかしこの変更したカーネルではinit起動後、0〜10秒でランダムなタイミング
でリセットしてしまう。キャッシュをオフにしても同じだ。SH4AのLDTLB命令は
TLBミス例外の後にMMUCR.URCを1を加算してからLDTLB命令を発行しないといけ
ない。これをちゃんとやってもだめ。(SH4Aでは他にメモリ割りつけのTLBをP2
からいじる必要はないP1でいい)

P3になったとして、例外が起こって、VAでpmap_kernel()のページテーブルペー
ジスロットを参照する。pmap_kernel()はP1にある。スロットは
pmap_kenter_paで既に登録したところなので絶対にある。そこのPTEもある。の
でそれをTLBに乗せるだけだ。

手間がかかるだけで問題はないはずなのだけど...。

これらはページテーブルページのインデックス、オフセットを仮想アドレスから
とりだすためのもの。
#define	__PMAP_PTP_SHIFT	22
#define	__PMAP_PTP_TRUNC(va)						\
	(((va) + (1 << __PMAP_PTP_SHIFT) - 1) & ~((1 << __PMAP_PTP_SHIFT) - 1))
#define	__PMAP_PTP_PG_N		(PAGE_SIZE / sizeof(pt_entry_t))
#define	__PMAP_PTP_INDEX(va)	(((va) >> __PMAP_PTP_SHIFT) & (__PMAP_PTP_N - 1))
#define	__PMAP_PTP_OFSET(va)	((va >> PGSHIFT) & (__PMAP_PTP_PG_N - 1))

これがカーネルのアドレス空間。P3のだけとしている。
VM_MIN_KERNEL_ADDRESS(0xc0000000)からVM_MAX_KERNEL_ADDRESS(0xe0000000)まで。
カーネル空間からはP0(0x0-0x80000000)も扱えるけれど、そこは使っていない。
使わなければ0x80000000より上ならカーネル空間、下ならユーザ空間と切りわけ
できて楽。実際それで空間を識別している。
struct pmap __pmap_kernel;
これはUVMに登録するための正式な名前にしている。
struct pmap *const kernel_pmap_ptr = &__pmap_kernel;
これはカーネル仮想空間のリミット。pmap_growkernelが呼び出されるとどんどん
増えていく。
STATIC vaddr_t __pmap_kve;	/* VA of last kernel virtual */
これらはkloaderが次にロードするカーネルをストアする領域をかき集める時のヒント
のために用意されている。なくてもいい。
// kloader uses avail_start and avail_end.
paddr_t avail_start;		/* PA of first available physical page */
paddr_t avail_end;		/* PA of last available physical page */
これはTLB例外が起きた時にできるだけ早くTLBにロードできるようにpmap_activate
で有効化されたアドレス空間のページテーブルページスロットを差している。
これはTTBレジスタ使った方が効率的かも。
/* For the fast tlb miss handler */
pt_entry_t **curptd;		/* p1 va of curlwp->...->pm_ptp */

これはstruct pmap自体をアロケートするためのプール。
/* pmap pool */
STATIC struct pool __pmap_pmap_pool;

これはstruct vm_pageのうちMD部分の拡張にあたる、
//	struct vm_page_md {
//		SLIST_HEAD(, pv_entry) pvh_head;
//		int pvh_flags;
//	};
につながれるリストになる。物理アドレスと仮想アドレスのマッピングが確定した
時に、そのvm_pageにつながれる。
/* pv_entry ops. */
struct pv_entry {
	struct pmap *pv_pmap;
	vaddr_t pv_va;
	SLIST_ENTRY(pv_entry) pv_link;
};

vm_pageはそのアドレスがメモリである時に(デバイスじゃない)、物理アドレス
に対して設定される。それに対して仮想アドレスは一意に決まらないので(複数
のプロセスから共有されているかもしれない)、アドレス空間(pv_pmap)、その
中でのオフセット(pv_va)の組で登録しておくことで、物理アドレスから、どこ
の仮想アドレスにマップしているかをリストをたぐればわかるようにしてある。

pmapとvaがあればそのpmapのページテーブルページからたどってPTEを探せば
物理アドレスは一意に決まる。

こういうマクロは好きじゃないな。俺が書いたのかもしれないが。
#define	__pmap_pv_alloc()	pool_get(&__pmap_pv_pool, PR_NOWAIT)
#define	__pmap_pv_free(pv)	pool_put(&__pmap_pv_pool, (pv))

これはMD拡張になるpv_entryを登録、抹消するためのもの。
STATIC void __pmap_pv_enter(pmap_t, struct vm_page *, vaddr_t);
STATIC void __pmap_pv_remove(pmap_t, struct vm_page *, vaddr_t);

これはそのpv_entryをプールからとってきたりするもの
STATIC void *__pmap_pv_page_alloc(struct pool *, int);
STATIC void __pmap_pv_page_free(struct pool *, void *);
そのプール。
STATIC struct pool __pmap_pv_pool;
ここでプールのアロケータに独自のを指定している。これはP1アドレスでpv_entry
をとるためのものだ。
STATIC struct pool_allocator pmap_pv_page_allocator = {
	__pmap_pv_page_alloc, __pmap_pv_page_free, 0,
};

/* ASID ops. */
STATIC int __pmap_asid_alloc(void);
STATIC void __pmap_asid_free(int);
STATIC struct {
	uint32_t map[8];
	int hint;	/* hint for next allocation */
} __pmap_asid;

これはPTEをアロケートする。PTPスロットがなければスロットにページテーブルページ
をアロケートして、そこからのオフセットを返す。
/* page table entry ops. */
STATIC pt_entry_t *__pmap_pte_alloc(pmap_t, vaddr_t);

これは今迄のマップを変更した時のものだけど、その実装はちょっと不明なと
ころがある。
/* pmap_enter util */
STATIC bool __pmap_map_change(pmap_t, vaddr_t, paddr_t, vm_prot_t,
    pt_entry_t);

void
pmap_bootstrap(void)
{
uvm_pageboot_allocはこの中でpmap_steal_memoryを呼び出しそのアドレスを確定する。
	/* Steal msgbuf area */
	initmsgbuf((void *)uvm_pageboot_alloc(MSGBUFSIZE), MSGBUFSIZE);
ここでインデックス0を最初とできるのはvmparm.hで
#define	VM_PHYSSEG_STRAT	VM_PSTRAT_BSEARCH
と指定しているので、uvm_page_physloadがアドレス順にしてくれているから。
	avail_start = ptoa(vm_physmem[0].start);
	avail_end = ptoa(vm_physmem[vm_nphysseg - 1].end);
	__pmap_kve = VM_MIN_KERNEL_ADDRESS;

	pmap_kernel()->pm_refcnt = 1;
	pmap_kernel()->pm_ptp = (pt_entry_t **)uvm_pageboot_alloc(PAGE_SIZE);
	memset(pmap_kernel()->pm_ptp, 0, PAGE_SIZE);

	/* Enable MMU */
	sh_mmu_start();
	/* Mask all interrupt */
	_cpu_intr_suspend();
	/* Enable exception for P3 access */
	_cpu_exception_resume(0);
}

vaddr_t
pmap_steal_memory(vsize_t size, vaddr_t *vstart, vaddr_t *vend)
{
//	printf("%s\n", __FUNCTION__);
	struct vm_physseg *bank;
	int i, j, npage;
	paddr_t pa;
	vaddr_t va;

	KDASSERT(!uvm.page_init_done);

	size = round_page(size);
	npage = atop(size);

#ifdef SH4A_EXT_ADDR32
uvm_pageboot_allocが必ずP1/P2にマップされるエリアのメモリを返す用に
その領域からのメモリを返します。これは
#define	VM_PHYSSEG_MAX		4
#define	VM_NFREELIST		2
#define	VM_FREELIST_P1ACCESS	1
のようにして、別々のフリーリストにメモリを登録した。
	// To assure uvm_pageboot_alloc allocates P1/P2 mapped area.
	for (i = 0, bank = &vm_physmem[i]; i < vm_nphysseg; i++, bank++)
		if ((npage <= bank->avail_end - bank->avail_start) &&
		    (bank->free_list == VM_FREELIST_P1ACCESS))
			break;
#else
	for (i = 0, bank = &vm_physmem[i]; i < vm_nphysseg; i++, bank++)
		if (npage <= bank->avail_end - bank->avail_start)
			break;
#endif
	KDASSERT(i != vm_nphysseg);

	/* Steal pages */
	pa = ptoa(bank->avail_start);
	bank->avail_start += npage;
	bank->start += npage;

	/* GC memory bank */
	if (bank->avail_start == bank->end) {
		/* Remove this segment from the list. */
		vm_nphysseg--;
		KDASSERT(vm_nphysseg > 0);
		for (j = i; i < vm_nphysseg; j++)
			vm_physmem[j] = vm_physmem[j + 1];
	}

	va = SH3_PHYS_TO_P1SEG(pa);
#ifdef SH4A_EXT_ADDR32
	assert(pa < 0x20000000);
#endif
	memset((void *)va, 0, size);

	return (va);
}

vaddr_t
pmap_growkernel(vaddr_t maxkvaddr)
{
//	printf("%s %d\n", __FUNCTION__, uvm.page_init_done);
	int i, n;

	if (maxkvaddr <= __pmap_kve)
		return (__pmap_kve);

	i = __PMAP_PTP_INDEX(__pmap_kve - VM_MIN_KERNEL_ADDRESS);
	__pmap_kve = __PMAP_PTP_TRUNC(maxkvaddr);
	n = __PMAP_PTP_INDEX(__pmap_kve - VM_MIN_KERNEL_ADDRESS);

	/* Allocate page table pages */
	for (;i < n; i++) {
		if (__pmap_kernel.pm_ptp[i] != NULL)
			continue;

		if (uvm.page_init_done) {
#ifdef SH4A_EXT_ADDR32
P1/P2アドレスが欲しいので、uvm_pagealloc_stratでフリーリストを指定して獲得
			struct vm_page *pg = uvm_pagealloc_strat(NULL, 0, NULL,
			    UVM_PGA_USERESERVE | UVM_PGA_ZERO,
			    UVM_PGA_STRAT_ONLY, VM_FREELIST_P1ACCESS);
			assert(VM_PAGE_TO_PHYS(pg) < 0x20000000);
#else
			struct vm_page *pg = uvm_pagealloc(NULL, 0, NULL,
			    UVM_PGA_USERESERVE | UVM_PGA_ZERO);
#endif
			if (pg == NULL)
				goto error;
			__pmap_kernel.pm_ptp[i] = (pt_entry_t *)
			    SH3_PHYS_TO_P1SEG(VM_PAGE_TO_PHYS(pg));
		} else {
			pt_entry_t *ptp = (pt_entry_t *)
			    uvm_pageboot_alloc(PAGE_SIZE);
			if (ptp == NULL)
				goto error;
			__pmap_kernel.pm_ptp[i] = ptp;
			memset(ptp, 0, PAGE_SIZE);
		}
	}

	return (__pmap_kve);
 error:
	panic("pmap_growkernel: out of memory.");
	/* NOTREACHED */
}

void
pmap_virtual_space(vaddr_t *start, vaddr_t *end)
{
//	printf("%s\n", __FUNCTION__);
	*start = VM_MIN_KERNEL_ADDRESS;
	*end = VM_MAX_KERNEL_ADDRESS;
}

void
pmap_init(void)
{
//	printf("%s\n", __FUNCTION__);
	/* Initialize pmap module */
#ifdef PMAP_MAP_POOLPAGE
	pool_init(&__pmap_pmap_pool, sizeof(struct pmap), 0, 0, 0, "pmappl",
	    &pool_allocator_nointr, IPL_NONE);
#else
プールがデフォルトでP3になるときにはアロケータを指定してstruct pmapをP1に
とるようにした。
	// allocate pmap to P1.
	pool_init(&__pmap_pmap_pool, sizeof(struct pmap), 0, 0, 0, "pmappl",
	    &pmap_pv_page_allocator, IPL_NONE);
#endif
	pool_init(&__pmap_pv_pool, sizeof(struct pv_entry), 0, 0, 0, "pvpl",
	    &pmap_pv_page_allocator, IPL_NONE);
	pool_setlowat(&__pmap_pv_pool, 16);
#if defined SH4 || defined SH4A //XXX should be  SH_HAS_VIRTUAL_ALIAS
	if (SH_HAS_VIRTUAL_ALIAS) {
		/*
		 * XXX
		 * Disable sosend_loan() in src/sys/kern/uipc_socket.c
		 * on SH4 to avoid possible virtual cache aliases and
		 * unnecessary map/unmap thrashing in __pmap_pv_enter().
		 * (also see comments in __pmap_pv_enter())
		 * 
		 * Ideally, read only shared mapping won't cause aliases
		 * so __pmap_pv_enter() should handle any shared read only
		 * mappings like ARM pmap.
		 */
		sock_loan_thresh = -1;
	}
#endif
}
pmap_t
pmap_create(void)
{
//	printf("%s\n", __FUNCTION__);
	pmap_t pmap;
	struct vm_page *pg;

	pmap = pool_get(&__pmap_pmap_pool, PR_WAITOK);
	memset(pmap, 0, sizeof(struct pmap));
	pmap->pm_asid = -1;
	pmap->pm_refcnt = 1;
	/* Allocate page table page holder (512 slot) */
#ifdef SH4A_EXT_ADDR32
ページテーブルページスロットはP1にとるようにします。
	pg = uvm_pagealloc_strat(NULL, 0, NULL,
	    UVM_PGA_USERESERVE | UVM_PGA_ZERO,
	    UVM_PGA_STRAT_ONLY, VM_FREELIST_P1ACCESS);
	assert(VM_PAGE_TO_PHYS(pg) < 0x20000000);
#else
	pg = uvm_pagealloc(NULL, 0, NULL, UVM_PGA_USERESERVE | UVM_PGA_ZERO);
#endif
ここでstruct pmapとページテーブルページスロットが別々になっているのはペー
ジテーブルページスロットは2KBなので、それと細いヘッダとプールを分けるこ
とでアロケータの効率を計る目的だろう。
	pmap->pm_ptp = (pt_entry_t **)SH3_PHYS_TO_P1SEG(VM_PAGE_TO_PHYS(pg));

	return (pmap);
}

void
pmap_destroy(pmap_t pmap)
{
//	printf("%s\n", __FUNCTION__);
	int i;

	if (--pmap->pm_refcnt > 0)
		return;

	/* Deallocate all page table page */
	for (i = 0; i < __PMAP_PTP_N; i++) {
		vaddr_t va = (vaddr_t)pmap->pm_ptp[i];
		if (va == 0)
			continue;
#ifdef DEBUG	/* Check no mapping exists. */
		{
			int j;
			pt_entry_t *pte = (pt_entry_t *)va;
			for (j = 0; j < __PMAP_PTP_PG_N; j++, pte++)
				KDASSERT(*pte == 0);
		}
#endif /* DEBUG */
		/* Purge cache entry for next use of this page. */
これはページテーブルページ。もう使われないのでinvalidateでいい。
が、ページテーブルページはP1にとっている。共有もしていない。次に
同じ物理ページをとるにしても、uvm_pagealloc->pmap_page_zeroでinvalidate
されるから、いらないのでは?
		if (SH_HAS_VIRTUAL_ALIAS)
			sh_dcache_inv_range(va, PAGE_SIZE);
		/* Free page table */
		uvm_pagefree(PHYS_TO_VM_PAGE(SH3_P1SEG_TO_PHYS(va)));
	}
	/* Deallocate page table page holder */
	if (SH_HAS_VIRTUAL_ALIAS)
		sh_dcache_inv_range((vaddr_t)pmap->pm_ptp, PAGE_SIZE);
	uvm_pagefree(PHYS_TO_VM_PAGE(SH3_P1SEG_TO_PHYS((vaddr_t)pmap->pm_ptp)));

	/* Free ASID */
	__pmap_asid_free(pmap->pm_asid);

	pool_put(&__pmap_pmap_pool, pmap);
}

void
pmap_reference(pmap_t pmap)
{
//	printf("%s\n", __FUNCTION__);
	pmap->pm_refcnt++;
}

void
pmap_activate(struct lwp *l)
{
//	printf("%s\n", __FUNCTION__);
	pmap_t pmap = l->l_proc->p_vmspace->vm_map.pmap;

	if (pmap->pm_asid == -1)
		pmap->pm_asid = __pmap_asid_alloc();

	KDASSERT(pmap->pm_asid >=0 && pmap->pm_asid < 256);

	sh_tlb_set_asid(pmap->pm_asid);
これはTLB例外の効率化のため。
	curptd = pmap->pm_ptp;
}

void
pmap_deactivate(struct lwp *l)
{
//	printf("%s\n", __FUNCTION__);
	/* Nothing to do */
}

int
pmap_enter(pmap_t pmap, vaddr_t va, paddr_t pa, vm_prot_t prot, u_int flags)
{
//	printf("%s pa=%lx va=%lx\n", __FUNCTION__, pa, va);
	struct vm_page *pg;
	struct vm_page_md *pvh;
	pt_entry_t entry, *pte;
	bool kva = (pmap == pmap_kernel());

	/* "flags" never exceed "prot" */
	KDASSERT(prot != 0 && ((flags & VM_PROT_ALL) & ~prot) == 0);

	pg = PHYS_TO_VM_PAGE(pa);
	entry = (pa & PG_PPN) | PG_4K;
	if (flags & PMAP_WIRED)
		entry |= _PG_WIRED;

	if (pg != NULL) {	/* memory-space */
		pvh = &pg->mdpage;
		entry |= PG_C;	/* always cached */

		/* Seed modified/reference tracking */
		if (flags & VM_PROT_WRITE) {
			entry |= PG_V | PG_D;
			pvh->pvh_flags |= PVH_MODIFIED | PVH_REFERENCED;
		} else if (flags & VM_PROT_ALL) {
			entry |= PG_V;
			pvh->pvh_flags |= PVH_REFERENCED;
		}

		/* Protection */
		if ((prot & VM_PROT_WRITE) && (pvh->pvh_flags & PVH_MODIFIED)) {
			if (kva)
				entry |= PG_PR_KRW | PG_SH;
			else
				entry |= PG_PR_URW;
		} else {
			/* RO or COW page */
			if (kva)
				entry |= PG_PR_KRO | PG_SH;
			else
				entry |= PG_PR_URO;
		}

		/* Check for existing mapping */
		if (__pmap_map_change(pmap, va, pa, prot, entry))
			return (0);

		/* Add to physical-virtual map list of this page */
		__pmap_pv_enter(pmap, pg, va);

	} else {	/* bus-space (always uncached map) */
		if (kva) {
			entry |= PG_V | PG_SH |
			    ((prot & VM_PROT_WRITE) ?
			    (PG_PR_KRW | PG_D) : PG_PR_KRO);
		} else {
			entry |= PG_V |
			    ((prot & VM_PROT_WRITE) ?
			    (PG_PR_URW | PG_D) : PG_PR_URO);
		}
	}

	/* Register to page table */
	if (kva)
		pte = __pmap_kpte_lookup(va);
	else {
		pte = __pmap_pte_alloc(pmap, va);
		if (pte == NULL) {
			if (flags & PMAP_CANFAIL)
				return ENOMEM;
			panic("pmap_enter: cannot allocate pte");
		}
	}

	*pte = entry;

	if (pmap->pm_asid != -1)
		sh_tlb_update(pmap->pm_asid, va, entry);

	if (!SH_HAS_UNIFIED_CACHE &&
	    (prot == (VM_PROT_READ | VM_PROT_EXECUTE)))
		sh_icache_sync_range_index(va, PAGE_SIZE);

	if (entry & _PG_WIRED)
		pmap->pm_stats.wired_count++;
	pmap->pm_stats.resident_count++;

	return (0);
}

/*
 * bool __pmap_map_change(pmap_t pmap, vaddr_t va, paddr_t pa,
 *     vm_prot_t prot, pt_entry_t entry):
 *	Handle the situation that pmap_enter() is called to enter a
 *	mapping at a virtual address for which a mapping already
 *	exists.
 */
bool
__pmap_map_change(pmap_t pmap, vaddr_t va, paddr_t pa, vm_prot_t prot,
    pt_entry_t entry)
{
//	printf("%s\n", __FUNCTION__);
	pt_entry_t *pte, oentry;
	vaddr_t eva = va + PAGE_SIZE;

	if ((pte = __pmap_pte_lookup(pmap, va)) == NULL ||
	    ((oentry = *pte) == 0))
		return (false);		/* no mapping exists. */

	if (pa != (oentry & PG_PPN)) {
		/* Enter a mapping at a mapping to another physical page. */
		pmap_remove(pmap, va, eva);
		return (false);
	}

	/* Pre-existing mapping */

	/* Protection change. */
	if ((oentry & PG_PR_MASK) != (entry & PG_PR_MASK))
		pmap_protect(pmap, va, eva, prot);

	/* Wired change */
	if (oentry & _PG_WIRED) {
		if (!(entry & _PG_WIRED)) {
			/* wired -> unwired */
			*pte = entry;
			/* "wired" is software bits. no need to update TLB */
			pmap->pm_stats.wired_count--;
		}
	} else if (entry & _PG_WIRED) {
		/* unwired -> wired. make sure to reflect "flags" */
		pmap_remove(pmap, va, eva);
		return (false);
	}

	return (true);	/* mapping was changed. */
}

/*
 * void __pmap_pv_enter(pmap_t pmap, struct vm_page *pg, vaddr_t vaddr):
 *	Insert physical-virtual map to vm_page.
 *	Assume pre-existed mapping is already removed.
 */
void
__pmap_pv_enter(pmap_t pmap, struct vm_page *pg, vaddr_t va)
{
	struct vm_page_md *pvh;
	struct pv_entry *pv;
	int s;

	s = splvm();
	if (SH_HAS_VIRTUAL_ALIAS) {
		/*
		 * Remove all other mappings on this physical page
		 * which have different virtual cache indexes to
		 * avoid virtual cache aliases.
		 *
		 * XXX We should also handle shared mappings which
		 * XXX have different virtual cache indexes by
		 * XXX mapping them uncached (like arm and mips do).
		 */

ここではvm_page(物理アドレス)に対してすでに他のアドレス空間でマップされ
ていて、キャッシュエイリアスが生じる場合に、その他のマップを外している。
pmap_removeでこの領域のキャッシュは書き戻される。コメントにもある通りこ
ういう事態が見つかった時にはこのページをアンキャッシュドにするという選
択もあるけれど、これが一番シンプルな解決方。

vm_pageにMDリストを追加するのはこれがやりたいため。
 again:
		pvh = &pg->mdpage;
		SLIST_FOREACH(pv, &pvh->pvh_head, pv_link) {
			if (sh_cache_indexof(va) !=
			    sh_cache_indexof(pv->pv_va)) {
				pmap_remove(pv->pv_pmap, pv->pv_va,
				    pv->pv_va + PAGE_SIZE);
				goto again;
			}
		}
	}

	/* Register pv map */
	pvh = &pg->mdpage;
	pv = __pmap_pv_alloc();
	pv->pv_pmap = pmap;
	pv->pv_va = va;

	SLIST_INSERT_HEAD(&pvh->pvh_head, pv, pv_link);
	splx(s);
}

void
pmap_remove(pmap_t pmap, vaddr_t sva, vaddr_t eva)
{
//	printf("%s\n", __FUNCTION__);
	struct vm_page *pg;
	pt_entry_t *pte, entry;
	vaddr_t va;

	KDASSERT((sva & PGOFSET) == 0);

	for (va = sva; va < eva; va += PAGE_SIZE) {
		if ((pte = __pmap_pte_lookup(pmap, va)) == NULL ||
		    (entry = *pte) == 0)
			continue;

		if ((pg = PHYS_TO_VM_PAGE(entry & PG_PPN)) != NULL)
			__pmap_pv_remove(pmap, pg, va);

		if (entry & _PG_WIRED)
			pmap->pm_stats.wired_count--;
		pmap->pm_stats.resident_count--;
		*pte = 0;

		/*
		 * When pmap->pm_asid == -1 (invalid ASID), old entry attribute
		 * to this pmap is already removed by pmap_activate().
		 */
		if (pmap->pm_asid != -1)
			sh_tlb_invalidate_addr(pmap->pm_asid, va);
	}
}

/*
 * void __pmap_pv_remove(pmap_t pmap, struct vm_page *pg, vaddr_t vaddr):
 *	Remove physical-virtual map from vm_page.
 */
void
__pmap_pv_remove(pmap_t pmap, struct vm_page *pg, vaddr_t vaddr)
{
//	printf("%s\n", __FUNCTION__);
	struct vm_page_md *pvh;
	struct pv_entry *pv;
	int s;

	s = splvm();
	pvh = &pg->mdpage;
	SLIST_FOREACH(pv, &pvh->pvh_head, pv_link) {
		if (pv->pv_pmap == pmap && pv->pv_va == vaddr) {
			if (SH_HAS_VIRTUAL_ALIAS ||
			    (SH_HAS_WRITEBACK_CACHE &&
				(pg->mdpage.pvh_flags & PVH_MODIFIED))) {
				/*
				 * Always use index ops. since I don't want to
				 * worry about address space.
				 */
				sh_dcache_wbinv_range_index
				    (pv->pv_va, PAGE_SIZE);
			}

			SLIST_REMOVE(&pvh->pvh_head, pv, pv_entry, pv_link);
			__pmap_pv_free(pv);
			break;
		}
	}
#ifdef DEBUG
	/* Check duplicated map. */
	SLIST_FOREACH(pv, &pvh->pvh_head, pv_link)
	    KDASSERT(!(pv->pv_pmap == pmap && pv->pv_va == vaddr));
#endif
	splx(s);
}

void
pmap_kenter_pa(vaddr_t va, paddr_t pa, vm_prot_t prot)
{
//	printf("%s va=%lx pa=%lx\n", __FUNCTION__, va, pa);
	pt_entry_t *pte, entry;

	KDASSERT((va & PGOFSET) == 0);
	KDASSERT(va >= VM_MIN_KERNEL_ADDRESS && va < VM_MAX_KERNEL_ADDRESS);
	entry = (pa & PG_PPN) | PG_V | PG_SH | PG_4K;
	if (prot & VM_PROT_WRITE)
		entry |= (PG_PR_KRW | PG_D);
	else
		entry |= PG_PR_KRO;

	if (PHYS_TO_VM_PAGE(pa))
		entry |= PG_C;

これが失敗しない理由がちょっとわからない。先んじてpmap_growkernelをしてる?
	if ((pte = __pmap_kpte_lookup(va)) == NULL) {
		printf ("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%s no pte\n",
		    __FUNCTION__);
	}

	KDASSERT(*pte == 0);
	*pte = entry;

	sh_tlb_update(0, va, entry);
//	sh4a_wired_counter_alloc (va);どのくらいエントリがあるのかカウント。
}

void
pmap_kremove(vaddr_t va, vsize_t len)
{
//	printf("%s va=%lx len=%lx\n", __FUNCTION__, va, len);
	pt_entry_t *pte;
	vaddr_t eva = va + len;

	KDASSERT((va & PGOFSET) == 0);
	KDASSERT((len & PGOFSET) == 0);
	KDASSERT(va >= VM_MIN_KERNEL_ADDRESS && eva <= VM_MAX_KERNEL_ADDRESS);

	for (; va < eva; va += PAGE_SIZE) {
		pte = __pmap_kpte_lookup(va);
		KDASSERT(pte != NULL);
		if (*pte == 0)
			continue;

		if (SH_HAS_VIRTUAL_ALIAS && PHYS_TO_VM_PAGE(*pte & PG_PPN))
			sh_dcache_wbinv_range(va, PAGE_SIZE);
		*pte = 0;

		sh_tlb_invalidate_addr(0, va);
//		sh4a_wired_counter_dealloc (va);どのくらいエントリがあるのかカウント。
	}
}

bool
pmap_extract(pmap_t pmap, vaddr_t va, paddr_t *pap)
{
//	printf("%s\n", __FUNCTION__);
	pt_entry_t *pte;

単純にアドレス空間と仮想アドレスのペアから物理アドレスを返すルーチン。
	/* handle P1 and P2 specially: va == pa */
	if (pmap == pmap_kernel() && (va >> 30) == 2) {
		if (pap != NULL)
			*pap = va & SH3_PHYS_MASK;

		return (true);
	}

	pte = __pmap_pte_lookup(pmap, va);
	if (pte == NULL || *pte == 0) {
//		printf("%s va=%lx failed\n", __FUNCTION__, va);
		return (false);
	}

	if (pap != NULL)
		*pap = (*pte & PG_PPN) | (va & PGOFSET);

	return (true);
}

void
pmap_protect(pmap_t pmap, vaddr_t sva, vaddr_t eva, vm_prot_t prot)
{
//	printf("%s\n", __FUNCTION__);
	bool kernel = pmap == pmap_kernel();
	pt_entry_t *pte, entry, protbits;
	vaddr_t va;

	sva = trunc_page(sva);

	if ((prot & VM_PROT_READ) == VM_PROT_NONE) {
		pmap_remove(pmap, sva, eva);
		return;
	}

	switch (prot) {
	default:
		panic("pmap_protect: invalid protection mode %x", prot);
		/* NOTREACHED */
	case VM_PROT_READ:
		/* FALLTHROUGH */
	case VM_PROT_READ | VM_PROT_EXECUTE:
		protbits = kernel ? PG_PR_KRO : PG_PR_URO;
		break;
	case VM_PROT_READ | VM_PROT_WRITE:
		/* FALLTHROUGH */
	case VM_PROT_ALL:
		protbits = kernel ? PG_PR_KRW : PG_PR_URW;
		break;
	}

	for (va = sva; va < eva; va += PAGE_SIZE) {

		if (((pte = __pmap_pte_lookup(pmap, va)) == NULL) ||
		    (entry = *pte) == 0)
			continue;

		if (SH_HAS_VIRTUAL_ALIAS && (entry & PG_D)) {
			if (!SH_HAS_UNIFIED_CACHE && (prot & VM_PROT_EXECUTE))
				sh_icache_sync_range_index(va, PAGE_SIZE);
			else
				sh_dcache_wbinv_range_index(va, PAGE_SIZE);
		}

		entry = (entry & ~PG_PR_MASK) | protbits;
		*pte = entry;

		if (pmap->pm_asid != -1)
			sh_tlb_update(pmap->pm_asid, va, entry);
	}
}

void
pmap_page_protect(struct vm_page *pg, vm_prot_t prot)
{
//	printf("%s\n", __FUNCTION__);
	struct vm_page_md *pvh = &pg->mdpage;
	struct pv_entry *pv;
	struct pmap *pmap;
	vaddr_t va;
	int s;

	switch (prot) {
	case VM_PROT_READ | VM_PROT_WRITE:
		/* FALLTHROUGH */
	case VM_PROT_ALL:
		break;

	case VM_PROT_READ:
		/* FALLTHROUGH */
	case VM_PROT_READ | VM_PROT_EXECUTE:
		s = splvm();
		SLIST_FOREACH(pv, &pvh->pvh_head, pv_link) {
			pmap = pv->pv_pmap;
			va = pv->pv_va;

			KDASSERT(pmap);
			pmap_protect(pmap, va, va + PAGE_SIZE, prot);
		}
		splx(s);
		break;

	default:
		/* Remove all */
		s = splvm();
		while ((pv = SLIST_FIRST(&pvh->pvh_head)) != NULL) {
			va = pv->pv_va;
			pmap_remove(pv->pv_pmap, va, va + PAGE_SIZE);
		}
		splx(s);
	}
}

void
pmap_unwire(pmap_t pmap, vaddr_t va)
{
//	printf("%s\n", __FUNCTION__);
	pt_entry_t *pte, entry;
このwireは物理アドレス-仮想アドレスのwire。メモリじゃないメモリマップドデバイス
だとvm_pageがない。しかしそういったマッピングはワイヤードに決まってる。

	if ((pte = __pmap_pte_lookup(pmap, va)) == NULL ||
	    (entry = *pte) == 0 ||
	    (entry & _PG_WIRED) == 0)
		return;

	*pte = entry & ~_PG_WIRED;
	pmap->pm_stats.wired_count--;
}

void
pmap_procwr(struct proc	*p, vaddr_t va, size_t len)
{
//	printf("%s\n", __FUNCTION__);
	if (!SH_HAS_UNIFIED_CACHE)
		sh_icache_sync_range_index(va, len);
}

void
pmap_zero_page(paddr_t phys)
{
//	printf("%s pa=%lx\n", __FUNCTION__, phys);
#ifdef SH4A_EXT_ADDR32
P1/P2アドレスならP2からやります。
	if (phys < 0x20000000)
		goto maped_area;
	struct vm_page *pg;
	struct vm_page_md *pvh;
	struct pv_entry *pv;

	bool vm_mapped = FALSE;
この物理ページに対応するvm_pageを探します。
	if ((pg = PHYS_TO_VM_PAGE(phys)) != NULL) {
		pvh = &pg->mdpage;
		// For each vm page which maps this physical page.
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
そのvm_pageに登録されている物理アドレスから0クリアと思ったけれど、これ
はだめだ。アドレス空間変更しないと。これじゃpmap_kenrel()のvaをゼロクリ
アしてしまう。アドレス空間変更してゼロにして、他のが見つかったら、そこ
のキャッシュをinvalidate.
		SLIST_FOREACH(pv, &pvh->pvh_head, pv_link) {
			memset ((void *)pv->pv_va, 0, PAGE_SIZE);
			vm_mapped = TRUE;
		}
	}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
	if (!vm_mapped) {
どこにもマップされていない。実際はほとんどこのケース。UVMはまずpmap_zero_page
してからpmap_enterする。
		// Mapped to P0 area
使ってないP0エリアにアンキャッシュドでマップしてそこでゼロクリア。
		vaddr_t va = 0;
		sh_tlb_update (0, va,
		    (phys & PG_PPN) | PG_PR_KRW | PG_V | PG_D | PG_4K | PG_SH);
		memset ((void *)va, 0, PAGE_SIZE);
マップを外します。
		sh_tlb_invalidate_addr (0, 0);
	}
	return;
maped_area:
#endif
	if (SH_HAS_VIRTUAL_ALIAS) {	/* don't polute cache */
		/* sync cache since we access via P2. */
これは皆殺し。ここでvm_pageを引っぱって、もしそvm_pageがあるならそこか
らinvalidateがいいだろう。
		sh_dcache_wbinv_all();
		memset((void *)SH3_PHYS_TO_P2SEG(phys), 0, PAGE_SIZE);
	} else {
		memset((void *)SH3_PHYS_TO_P1SEG(phys), 0, PAGE_SIZE);
	}
}

ここも大体同じ方針。ここもアドレス空間をよく考えて全部書き直し。
void
pmap_copy_page(paddr_t src, paddr_t dst)
{
//	printf("***************************************%s\n", __FUNCTION__);
#ifdef SH4A_EXT_ADDR32
	if (src < 0x20000000 && dst < 0x20000000)
		goto maped_area;
//	printf("************TMPMAP**********************%s\n", __FUNCTION__);
	struct vm_page *pg_src, *pg_dst;
	vaddr_t dst_va;
	vaddr_t src_va;
	bool vm_mapped = FALSE;
	struct pv_entry *pv_src = 0;
	struct pv_entry *pv_dst = 0;

	pg_src = PHYS_TO_VM_PAGE(src);
	pg_dst = PHYS_TO_VM_PAGE(dst);
	if (pg_src && pg_dst) {
		struct vm_page_md *pvh;
		pvh = &pg_src->mdpage;
		pv_src = SLIST_FIRST(&pvh->pvh_head);
		pvh = &pg_dst->mdpage;
		pv_dst = SLIST_FIRST(&pvh->pvh_head);
		if (pv_src && pv_dst) {
			dst_va = pv_dst->pv_va;
			src_va = pv_src->pv_va;
			memcpy ((void *)dst_va, (void *)src_va, PAGE_SIZE);
			vm_mapped = TRUE;
		}
	}
	if (!vm_mapped) {	// Temporary mapped to P0
		if (pv_src)
			sh_dcache_wb_range (pv_src->pv_va, PAGE_SIZE);
		if (pv_dst)
			sh_dcache_inv_range (pv_dst->pv_va, PAGE_SIZE);
		src_va = 0;
		dst_va = PAGE_SIZE;
		sh_tlb_update(0, src_va,
		    (src & PG_PPN) | PG_PR_KRW | PG_V | PG_D | PG_4K | PG_SH);
		sh_tlb_update(0, dst_va,
		    (src & PG_PPN) | PG_PR_KRW | PG_V | PG_D | PG_4K | PG_SH);
		memcpy((void *)dst_va, (void *)src_va, PAGE_SIZE);
		sh_tlb_invalidate_addr (0, src_va);
		sh_tlb_invalidate_addr (0, dst_va);
	}
	return;
maped_area:
#endif
	if (SH_HAS_VIRTUAL_ALIAS) {	/* don't polute cache */
		/* sync cache since we access via P2. */
		sh_dcache_wbinv_all();
		memcpy((void *)SH3_PHYS_TO_P2SEG(dst),
		    (void *)SH3_PHYS_TO_P2SEG(src), PAGE_SIZE);
	} else {
		memcpy((void *)SH3_PHYS_TO_P1SEG(dst),
		    (void *)SH3_PHYS_TO_P1SEG(src), PAGE_SIZE);
	}
//	printf("DONE***************************************%s\n", __FUNCTION__);
}

bool
pmap_is_referenced(struct vm_page *pg)
{
//	printf("%s\n", __FUNCTION__);
	return ((pg->mdpage.pvh_flags & PVH_REFERENCED) ? true : false);
}

bool
pmap_clear_reference(struct vm_page *pg)
{
//	printf("%s\n", __FUNCTION__);
	struct vm_page_md *pvh = &pg->mdpage;
	struct pv_entry *pv;
	pt_entry_t *pte;
	pmap_t pmap;
	vaddr_t va;
	int s;

	if ((pg->mdpage.pvh_flags & PVH_REFERENCED) == 0)
		return (false);

	pg->mdpage.pvh_flags &= ~PVH_REFERENCED;

	s = splvm();
	/* Restart reference bit emulation */
	SLIST_FOREACH(pv, &pvh->pvh_head, pv_link) {
		pmap = pv->pv_pmap;
		va = pv->pv_va;

		if ((pte = __pmap_pte_lookup(pmap, va)) == NULL)
			continue;
		if ((*pte & PG_V) == 0)
			continue;
		*pte &= ~PG_V;

		if (pmap->pm_asid != -1)
			sh_tlb_invalidate_addr(pmap->pm_asid, va);
	}
	splx(s);

	return (true);
}

bool
pmap_is_modified(struct vm_page *pg)
{
//	printf("%s\n", __FUNCTION__);
	return ((pg->mdpage.pvh_flags & PVH_MODIFIED) ? true : false);
}

bool
pmap_clear_modify(struct vm_page *pg)
{
//	printf("%s\n", __FUNCTION__);
	struct vm_page_md *pvh = &pg->mdpage;
	struct pv_entry *pv;
	struct pmap *pmap;
	pt_entry_t *pte, entry;
	bool modified;
	vaddr_t va;
	int s;

	modified = pvh->pvh_flags & PVH_MODIFIED;
	if (!modified)
		return (false);

	pvh->pvh_flags &= ~PVH_MODIFIED;

	s = splvm();
	if (SLIST_EMPTY(&pvh->pvh_head)) {/* no map on this page */
		splx(s);
		return (true);
	}

	/* Write-back and invalidate TLB entry */
	if (!SH_HAS_VIRTUAL_ALIAS && SH_HAS_WRITEBACK_CACHE)
		sh_dcache_wbinv_all();

	SLIST_FOREACH(pv, &pvh->pvh_head, pv_link) {
		pmap = pv->pv_pmap;
		va = pv->pv_va;
		if ((pte = __pmap_pte_lookup(pmap, va)) == NULL)
			continue;
		entry = *pte;
		if ((entry & PG_D) == 0)
			continue;

		if (SH_HAS_VIRTUAL_ALIAS)
			sh_dcache_wbinv_range_index(va, PAGE_SIZE);

		*pte = entry & ~PG_D;
		if (pmap->pm_asid != -1)
			sh_tlb_invalidate_addr(pmap->pm_asid, va);
	}
	splx(s);

	return (true);
}

paddr_t
pmap_phys_address(paddr_t cookie)
{
//	printf("%s\n", __FUNCTION__);
	return (sh3_ptob(cookie));
}

#if defined SH4 || defined SH4A //XXX should be  SH_HAS_VIRTUAL_ALIAS
/*
 * pmap_prefer(vaddr_t foff, vaddr_t *vap)
 *
 * Find first virtual address >= *vap that doesn't cause
 * a virtual cache alias against vaddr_t foff.
 */
void
pmap_prefer(vaddr_t foff, vaddr_t *vap)
{
//	printf("%s\n", __FUNCTION__);
	vaddr_t va;

	if (SH_HAS_VIRTUAL_ALIAS) {
		va = *vap;

		*vap = va + ((foff - va) & sh_cache_prefer_mask);
	}
}
#endif /* SH_HAS_VIRTUAL_ALIAS */

/*
 * pv_entry pool allocator:
 *	void *__pmap_pv_page_alloc(struct pool *pool, int flags):
 *	void __pmap_pv_page_free(struct pool *pool, void *v):
 */
void *
__pmap_pv_page_alloc(struct pool *pool, int flags)
{
//	printf("%s\n", __FUNCTION__);
	struct vm_page *pg;
#ifdef SH4A_EXT_ADDR32
	pg = uvm_pagealloc_strat(NULL, 0, NULL, UVM_PGA_USERESERVE,
	    UVM_PGA_STRAT_ONLY, VM_FREELIST_P1ACCESS);
	assert(VM_PAGE_TO_PHYS(pg) < 0x20000000);
#else
	pg = uvm_pagealloc(NULL, 0, NULL, UVM_PGA_USERESERVE);
#endif
	if (pg == NULL)
		return (NULL);

	return ((void *)SH3_PHYS_TO_P1SEG(VM_PAGE_TO_PHYS(pg)));
}

void
__pmap_pv_page_free(struct pool *pool, void *v)
{
//	printf("%s\n", __FUNCTION__);
	vaddr_t va = (vaddr_t)v;

	/* Invalidate cache for next use of this page */
	if (SH_HAS_VIRTUAL_ALIAS)
		sh_icache_sync_range_index(va, PAGE_SIZE);
	uvm_pagefree(PHYS_TO_VM_PAGE(SH3_P1SEG_TO_PHYS(va)));
}

/*
 * pt_entry_t __pmap_pte_alloc(pmap_t pmap, vaddr_t va):
 *	lookup page table entry. if found returns it, else allocate it.
 *	page table is accessed via P1.
 */
pt_entry_t *
__pmap_pte_alloc(pmap_t pmap, vaddr_t va)
{
//	printf("%s\n", __FUNCTION__);
	struct vm_page *pg;
	pt_entry_t *ptp, *pte;

	if ((pte = __pmap_pte_lookup(pmap, va)) != NULL)
		return (pte);

	/* Allocate page table (not managed page) */
#ifdef SH4A_EXT_ADDR32
	pg = uvm_pagealloc_strat(NULL, 0, NULL,
	    UVM_PGA_USERESERVE | UVM_PGA_ZERO,
	    UVM_PGA_STRAT_ONLY, VM_FREELIST_P1ACCESS);
	assert(VM_PAGE_TO_PHYS(pg) < 0x20000000);
#else
	pg = uvm_pagealloc(NULL, 0, NULL, UVM_PGA_USERESERVE | UVM_PGA_ZERO);
#endif
	if (pg == NULL)
		return NULL;

	ptp = (pt_entry_t *)SH3_PHYS_TO_P1SEG(VM_PAGE_TO_PHYS(pg));
	pmap->pm_ptp[__PMAP_PTP_INDEX(va)] = ptp;

	return (ptp + __PMAP_PTP_OFSET(va));
}

/*
 * pt_entry_t *__pmap_pte_lookup(pmap_t pmap, vaddr_t va):
 *	lookup page table entry, if not allocated, returns NULL.
 */
pt_entry_t *
__pmap_pte_lookup(pmap_t pmap, vaddr_t va)
{
////	printf("%s\n", __FUNCTION__);
	pt_entry_t *ptp;

	if (pmap == pmap_kernel())
		return (__pmap_kpte_lookup(va));

	/* Lookup page table page */
	ptp = pmap->pm_ptp[__PMAP_PTP_INDEX(va)];
	if (ptp == NULL)
		return (NULL);

	return (ptp + __PMAP_PTP_OFSET(va));
}


/*
 * pt_entry_t *__pmap_kpte_lookup(vaddr_t va):
 *	kernel virtual only version of __pmap_pte_lookup().
 */
pt_entry_t *
__pmap_kpte_lookup(vaddr_t va)
{
	pt_entry_t *ptp;
//	printf("%s va=%lx, %lx\n", __FUNCTION__, va, __PMAP_PTP_INDEX(va-VM_MIN_KERNEL_ADDRESS));
	ptp = __pmap_kernel.pm_ptp[__PMAP_PTP_INDEX(va-VM_MIN_KERNEL_ADDRESS)];
	if (ptp == NULL)
		return NULL;

//	printf("%s found %p\n", __FUNCTION__, ptp + __PMAP_PTP_OFSET(va));
	return (ptp + __PMAP_PTP_OFSET(va));
}

/*
 * bool __pmap_pte_load(pmap_t pmap, vaddr_t va, int flags):
 *	lookup page table entry, if found it, load to TLB.
 *	flags specify do emulate reference and/or modified bit or not.
 */
bool
__pmap_pte_load(pmap_t pmap, vaddr_t va, int flags)
{
//	printf("%s\n", __FUNCTION__);
	struct vm_page *pg;
	pt_entry_t *pte;
	pt_entry_t entry;

	KDASSERT((((int)va < 0) && (pmap == pmap_kernel())) ||
	    (((int)va >= 0) && (pmap != pmap_kernel())));

	/* Lookup page table entry */
	if (((pte = __pmap_pte_lookup(pmap, va)) == NULL) ||
	    ((entry = *pte) == 0))
		return (false);

	KDASSERT(va != 0);

	/* Emulate reference/modified tracking for managed page. */
	if (flags != 0 && (pg = PHYS_TO_VM_PAGE(entry & PG_PPN)) != NULL) {
		if (flags & PVH_REFERENCED) {
			pg->mdpage.pvh_flags |= PVH_REFERENCED;
			entry |= PG_V;
		}
		if (flags & PVH_MODIFIED) {
			pg->mdpage.pvh_flags |= PVH_MODIFIED;
			entry |= PG_D;
		}
		*pte = entry;
	}

	/* When pmap has valid ASID, register to TLB */
	if (pmap->pm_asid != -1)
		sh_tlb_update(pmap->pm_asid, va, entry);

	return (true);
}

/*
 * int __pmap_asid_alloc(void):
 *	Allocate new ASID. if all ASID is used, steal from other process.
 */
int
__pmap_asid_alloc(void)
{
//	printf("%s\n", __FUNCTION__);
	struct proc *p;
	int i, j, k, n, map, asid;

	/* Search free ASID */
	i = __pmap_asid.hint >> 5;
	n = i + 8;
	for (; i < n; i++) {
		k = i & 0x7;
		map = __pmap_asid.map[k];
		for (j = 0; j < 32; j++) {
			if ((map & (1 << j)) == 0 && (k + j) != 0) {
				__pmap_asid.map[k] |= (1 << j);
				__pmap_asid.hint = (k << 5) + j;
				return (__pmap_asid.hint);
			}
		}
	}

	/* Steal ASID */
	LIST_FOREACH(p, &allproc, p_list) {
		if ((asid = p->p_vmspace->vm_map.pmap->pm_asid) > 0) {
			pmap_t pmap = p->p_vmspace->vm_map.pmap;
			pmap->pm_asid = -1;
			__pmap_asid.hint = asid;
			/* Invalidate all old ASID entry */
			sh_tlb_invalidate_asid(pmap->pm_asid);

			return (__pmap_asid.hint);
		}
	}

	panic("No ASID allocated.");
	/* NOTREACHED */
}

/*
 * void __pmap_asid_free(int asid):
 *	Return unused ASID to pool. and remove all TLB entry of ASID.
 */
void
__pmap_asid_free(int asid)
{
	int i;

	if (asid < 1)	/* Don't invalidate kernel ASID 0 */
		return;

	sh_tlb_invalidate_asid(asid);

	i = asid >> 5;
	__pmap_asid.map[i] &= ~(1 << (asid - (i << 5)));
}



菜園状況。断腸の思いで大根を一本間引き。大根は2/3がヨトウムシにやられて
しまい、それでもなんとか残ったものなのでとても気が引けた。

気合い入れて深くまで耕しただけあって、根の伸びがいいような。はじめての 大根だからわからないけれど。50cmくらい掘った下に鶏糞を入れてあるんだ。

SH4A続き。ちょっと疲れてきた。とりあえずNetBSD動かしておこうかと思った ものの、いろいろはまってしまいなかなか進まなかった。コンパイル回数に苦 渋が見てとれるだろう...。まだプロンプトが出たあとで割り込みがあがってし まって入力できない。32bitモードだとここまでくるまでにセグメンテーション フォルトになってしまう。
Copyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
    2006, 2007, 2008, 2009
    The NetBSD Foundation, Inc.  All rights reserved.
Copyright (c) 1982, 1986, 1989, 1991, 1993
    The Regents of the University of California.  All rights reserved.

NetBSD 5.99.20 (RENESAS7785LCR) #518: Sun Oct 18 23:02:13 JST 2009
        uch@alexandrite:/usr/src/sys/arch/evbsh3/compile/RENESAS7785LCR
general exception handler:      324 byte
TLB miss exception handler:     368 byte
interrupt exception handler:    288 byte
total memory = 112 MB
avail memory = 105 MB
Renesas evaluation board R0P7785LC0011RL
mainbus0 (root)
cpu0 at mainbus0: SH4A 599.999 MHz PCLOCK 49.999 MHz
cpu0: 32KB/32B 4-way set-associative Instruction cache.
cpu0: 32KB/32B 4-way set-associative Data cache.
cpu0: U0, P0, P3 write-back; P1 write-back
cpu0: full-associative 4 ITLB, 64 UTLB entries
cpu0: multiple virtual storage mode, SQ access: kernel/user, wired 3
cpu0: 29bit physical address mode. TLB-compatible mode.
shb0 at mainbus0
scif0 at shb0
scif0: console
rn_init: radix functions require max_keylen be set
root on md0a dumps on md0b
mountroot: trying ffs...
root file system type: ffs
WARNING: no TOD clock present
WARNING: using filesystem time
WARNING: CHECK AND RESET THE DATE!
init: copying out flags `-s' 3
init: copying out path `/sbin/init' 11
erase ^?, werase ^W, kill ^U, intr ^C

mount_kernfs: kernfs on /kern: Operation not supported by device
# 



CR85の整備。カウル、チェーン、クランクケース洗いました。ピストン、シリ
ンダも問題なし。今年もついに残り一戦。MCFAJ最終戦筑波。暇見てピストン作
りに行っておかないと。そして次までにはP-LAP本体を外して全てロガーから駆
動してみよう。



菜園状況。ヨトウムシの被害を免れた大根はとても順調です。本当は間引きし た方がいいような間隔だけれど...。間引きした先が全部やられてしまったの だ...。

キャベツの苗。毎日モンシロチョウの産卵攻撃にあっています。まめに調べて 卵を落とし、青虫を殺してます。

SH4A続き。マイOSのVMまわりはいずれにしよう。ここは難しいし。じっくりや りたい。
ということでデバイスの方に。まずはdelayを作ろう。(まだ作ってなかった。) SH7785は6chもタイマがあるので、この1chをもらってdelayにした。この評価基 板R0P7785LC0011RLは入力クロックが33.333MHz。ここから逓倍、分周してPCLK は50MHz。TMUはこれの4,16,64,256の分周でカウントできる。(TMUを外部クロッ クで駆動することも可能)
このあたりは昔ながらの日立なのだけど、LPC2388の便利なタイマを使った後だ とちょっと不満。LPC2388だと、PCLKのカウンタリミットを設定してタイマカウ ンタの更新ができるので、とても自由に設定できる。
void
udelay (uint16_t n)
{
  cpu_status_t s = intr_suspend ();

  *TSTR1 &= ~TSTR_STR5;	// Stop TMU5
  *TCNT5 = n * udelay_param;
  *TCOR5 = 0;
  *TCR5 = TPSC_PCK_4;
  *TSTR1 |= TSTR_STR5;	// Start TMU5
  while ((*TCR5 & TCR_UNF) == 0)
    ;
  *TSTR1 &= ~TSTR_STR5;	// Stop TMU5
  intr_resume (s);
}


SH4A続き。簡単なデマンドページングのVMを作ってみます。

デマンドページングの入口となるのはTLBミス例外。今回はカーネルモードのみ、
レジスタバンク1のみを使っている。なので例外に入った時のバンク切換えはないのでそのままそのスタックに退避している。例外が禁止
のまま実行です。ということでこの例外処理の間にさらにTLBミスすることはさ
せない方針です。これは単純化のため。

#include <asm/asm.h>
	.section	.vector_tlbmiss, "ax"	// "A"llocate. e"X"ecute.
	.align	2
	mov.l	r0,	@-r15
	mov.l	r1,	@-r15
	mov.l	r2,	@-r15
	mov.l	r3,	@-r15
	mov.l	r4,	@-r15
	mov.l	r5,	@-r15
	mov.l	r6,	@-r15
	mov.l	r7,	@-r15
	mov.l	r8,	@-r15
	mov.l	r9,	@-r15
	mov.l	r10,	@-r15
	mov.l	r11,	@-r15
	mov.l	r12,	@-r15
	mov.l	r13,	@-r15
	mov.l	r14,	@-r15
	sts.l	macl,	@-r15
	sts.l	mach,	@-r15
	sts.l	pr,	@-r15

	// All register saved.
引数にTLBミスを起こしたアドレスのVPNを渡します。
	mov.l	.L_PTEH, r0
	mov.l	.L_pager_load_page, r1
	jsr	@r1
	 mov.l	@r0,	r4
	lds.l	@r15+,	pr
	lds.l	@r15+,	mach
	lds.l	@r15+,	macl
	mov.l	@r15+,	r14
	mov.l	@r15+,	r13
	mov.l	@r15+,	r12
	mov.l	@r15+,	r11
	mov.l	@r15+,	r10
	mov.l	@r15+,	r9
	mov.l	@r15+,	r8
	mov.l	@r15+,	r7
	mov.l	@r15+,	r6
	mov.l	@r15+,	r5
	mov.l	@r15+,	r4
	mov.l	@r15+,	r3
	mov.l	@r15+,	r2
	mov.l	@r15+,	r1
	rte
	 mov.l	@r15+,	r0
	.align	2
.L_PTEH:
	.long	0xff000000
.L_pager_load_page:
	.long	_pager_load_page


そのハンドリングは

#include <system.h>
#include <console.h>
#include <fpool.h>
#include <pte.h>
#include <vm.h>
#include <cpu.h>
#include <mmu.h>
#include <string.h>

アドレス変換されるP0領域とP3にそれぞれページテーブルページのディレクト
リを作ります。
struct pte ptp_slot_p0[PTP_ENTRY];
struct pte ptp_slot_p3[PTP_ENTRY];

void pte_setup (struct pte *, paddr_t);
void pager_tlb_update (struct pte *, vaddr_t);
void alloc_page_p3 (struct vm_page **, struct pte *);

#define	PTP_INDEX_OF(x) (((x) >> PTP_INDEX_SHIFT) & PTP_INDEX_MASK)

void
pager_load_page (vaddr_t vaddr)
{
  struct pte *ptp;
  struct vm_page *vm;

  iprintf ("%s va=%x ", __FUNCTION__, vaddr);

例外を起こしたアドレスがP0かP3かを分けます。
  if (vaddr & 0x80000000)
    {
      iprintf ("P3\n");
P3の時は0xc0000000からなので、マスクします。
      ptp = &ptp_slot_p3[PTP_INDEX_OF (vaddr & 0x1fffffff)];
    }
  else
    {
      iprintf ("P0\n");
      ptp = &ptp_slot_p0[PTP_INDEX_OF (vaddr)];
    }

  if (ptp->ptel == 0)	// Page table page not allocated.
    {
ページテーブルページが確保されてなかったらこれから確保します。
これはP3に任意にマップします。(P0は2G全てマップできるように)
      alloc_page_p3 (&vm, ptp);
      iprintf ("PTP allocated pa=%x va=%x\n", vm->pa, vm->va);
    }
  else
    {
すでにページテーブルページがあったのなら、その仮想アドレスを
とります。ここで参照しているけれど、結構ロス。PTEに仮想アドレスも
乗せてしまった方が効率的かも。

 物理アドレス29bitモードだけだったSH4までなら、全てのメモリはP1/P2から
アクセスできたので、物理アドレスがわかれば対応するP1/P2アドレスに変換し
てアクセスという手段が使えた(NetBSDの実装はそう)。しかしSH4Aの物理アド
レス32bitモードになると、必ずしもP1/P2からアクセスできるとは限らないの
で、仮想アドレスでアクセスする必要がある。あるいは、P1,P2でアクセスでき
る領域のアロケータを用意してそこからひっぱるか。
      vm = vm_lookup_by_paddr (ptp->ptel & PTEL_PPN);

このルーチンではTLBミスを起こさないという前提なので、確実にTLBに乗せま
す。もし既に乗っていたらTLB多重ヒット例外を起こしてしまうので、まず確実
にTLBから落とします。
      sh4a_tlb_invalidate_addr (0, vm->va);
そして乗せます。これでページテーブルページがアクセスできるようになった。
      pager_tlb_update (ptp, vm->va);
      iprintf ("PTP found. pa=%x va=%x\n", ptp->ptel & PTEL_PPN, vm->va);
    }
  // Page table page is setuped.

  // Target page table entry.
これは例外を起こしたアドレスに該当するページのエントリです。
  struct pte *pte = (struct pte *)vm_vaddr (vm) +
    ((vaddr >> PTP_OFFSET_SHIFT) & PTP_OFFSET_MASK);
  iprintf ("PTE=%x\n", pte);

そのページがまだ登録されてなければ、物理ページを拾ってきてマップします。
  if (pte->ptel == 0)
    {
      // not mapped area.
      paddr_t pa = fpool_get_page ();
      pte_setup (pte, pa);
      vm = vm_page_allocate ();
      vm_page_register (vm, vaddr, pa);
      iprintf ("New map pa=%x va=%x\n", pa, vaddr);
   }

そしてTLBに乗せます。ここで例外から終了して、この仮想アドレスにアクセス
できる。
  pager_tlb_update (pte, vaddr);
}

void
pte_setup (struct pte *pte, paddr_t pa)
{
  uint32_t ptel, ptea;

  ptel = pa & PTEL_PPN;
  ptea = 0;
  sh4a_tlb_entry_pagesize (PG_8K, &ptel, &ptea);
  sh4a_tlb_entry_protect (P_RW, &ptel, &ptea);
  sh4a_tlb_entry_cache (WRITEBACK, &ptel, &ptea);
さらにTLBミスを起こさない(PTE_V)、書き込み可(PTE_D)設定に。
  pte->ptel = ptel | PTEL_D | PTEL_V;
  pte->ptea = ptea;
}

void
pager_tlb_update (struct pte *pte, vaddr_t va)
{

このカウンタのインクリメントはいらないかも。これは連続してLDTLBをすると
同じエントリを上書きしてしまうのを防ぐためのもの。
  // Increment UTLB counter.
  int cnt = (sh4a_tlb_counter_get () + 1) & MMUCR_URC_MASK;
  sh4a_tlb_counter_set (cnt);

  *SH4A_PTEH = va & PTEH_VPN;
  *SH4A_PTEL = pte->ptel;
#ifdef SH4A_EXT_MMU
  *SH4A_PTEA = pte->ptea;
#endif
  __asm volatile ("ldtlb");
  CPU_SYNC_RESOURCE ();
}

void
alloc_page_p3 (struct vm_page **vmp, struct pte *ptep)
{
  struct vm_page *vm;
  struct pte *pte;
  paddr_t pa;
  vaddr_t va;

  pa = fpool_get_page ();適当に物理ページを拾ってきて、
  vm = vm_page_allocate ();仮想ページの管理構造体を確保。
  va = vm_page_map_p3 (vm);ここで適当なP3のアドレスを拾って設定。
  vm_page_register (vm, va, pa);物理-仮想を確定。
  // Load entry to the TLB. この設定をTLBに設定。
  pte = &ptp_slot_p3[PTP_INDEX_OF (va & 0x1fffffff)];
  pte_setup (pte, pa);
  pager_tlb_update (pte, va);
  // Clear page table page.
  memset ((void *)va, 0, PAGE_SIZE);

  *vmp = vm;
  *ptep = *pte;
}


vmはまず簡単にこの程度。
struct vm_page __vm_pool[1024];プールの数はとりあえず適当に十分なところで。
int __vm_pool_index;
vaddr_t vm_base = 0xc0000000;このVMから任意にマップされる仮想アドレスはP3です。

struct vm_page *
vm_page_allocate ()
{
  struct vm_page *vm = __vm_pool + __vm_pool_index++;

  return vm;
}

vaddr_t
vm_page_map_p3 (struct vm_page *vm)
{

  vm->va = vm_base;
  vm_base += PAGE_SIZE;

  return vm->va;
}

struct vm_page *
vm_lookup_by_paddr (paddr_t paddr)
{
  int i;

  for (i = 0; i < 1024; i++)
    if (__vm_pool[i].pa == paddr)
      {
	iprintf ("%s pa %x -> va %x\n", __FUNCTION__,
		 __vm_pool[i].pa, __vm_pool[i].va);
	return __vm_pool + i;
      }

  return 0;
}

void
vm_page_register (struct vm_page *vm, vaddr_t va, paddr_t pa)
{

  vm->va = va;
  vm->pa = pa;
}

vaddr_t
vm_vaddr (struct vm_page *vm)
{

  return vm->va;
}


物理アドレスの固定長プールも簡単にこの程度。ただ順々にページサイズごと
に出すだけ。

static paddr_t pool;
static psize_t limit;

void
fpool_load_memory (paddr_t a, psize_t sz)
{
  pool = ROUND_PAGE (a);
  limit = TRUNC_PAGE (a + sz);
  iprintf ("pool memory %x-%x\n", pool, limit);
}

paddr_t
fpool_get_page ()
{
  paddr_t a = 0;

  if (pool < limit)
    {
      a = pool;
      pool += PAGE_SIZE;
    }

  return a;
}

テスト。

=> go 0x89000000
## Starting application at 0x89000000 ...
stack_start: 0x89100000
RAM data: 0x8900819c-0x89008708 1388byte
bss: 0x89008720-0x8900f4b0 28048byte
Privilege-mode, bank 1, Exception disabled, FPU enabled, IMASK=0xf
bss memory check passed.
PMB page size 16MB mask=ffffff VPN:a4000000 PPN:4000000
PMB page size 128MB mask=7ffffff VPN:90000000 PPN:58000000
md_thread_create: [2]:uart recv pc=89004924 sp=8900bf78 stack=1024byte
thread_start: [2]
md_thread_create: [3]:uart send pc=890048b4 sp=8900c450 stack=1024byte
thread_start: [3]
md_thread_create: [4]:test pc=8900199c sp=8900b27c stack=1024byte
thread_start: [4]
test_thread:1234abcd
INTC: fffffff6 1f000000
hello 0
ここでVMをスタート。
test4> vm
pool memory 5c000000-60000000
この段階ではTLBにはなにもなし。
test4> tlbdump                                                  
MMUCR: 2c000081 PTEH: 0 PTEL: 0 TTB: 0 TEA: 0 PTEA: 0
--------------------ITLB--------------------
(-|V)  16M -- WB VPN:89000000 PPN:9000000 PMB [3]
--------------------UTLB--------------------
ここで仮想アドレス0をアクセス。
test4> mem r 0
TLBミス例外が起きて
pager_load_page va=0 P0
ページテーブルページを確保して、
PTP allocated pa=5c000000 va=c0000000
PTE=c0000000
そこに物理ページ5c002000を拾ってきて仮想アドレス0にマップ
New map pa=5c002000 va=0
例外終了。
(r)0: ac1dcafe
test4> tlbdump
MMUCR: 2c003881 PTEH: 0 PTEL: 5c00210c TTB: 0 TEA: 0 PTEA: 0
--------------------ITLB--------------------
(-|V)  16M -- WB VPN:89000000 PPN:9000000 PMB [3]
--------------------UTLB--------------------
これはP3のページテーブルページ。
(D|V) [rwx][---]   8K -- WB -- VPN:c0000000 PPN:5c000000 ASID:0 [1]
これはP0に今回新しくマップしたところ。
(D|V) [rwx][---]   8K -- WB -- VPN:0 PPN:5c002000 ASID:0 [13]
既にTLBに乗っているので例外は起きない。
test4> mem r 0
(r)0: ac1dcafe
ここでTLBを全てクリア。
test4> tlbclear
test4> tlbdump                                                  
MMUCR: 78003c81 PTEH: 0 PTEL: 5c00210c TTB: 0 TEA: 0 PTEA: 0
--------------------ITLB--------------------
(-|V)  16M -- WB VPN:89000000 PPN:9000000 PMB [2]
--------------------UTLB--------------------
もう一度仮想アドレス0を読んでみます。
test4> mem r 0
pager_load_page va=0 P0
既にページテーブルページはあるので、そこをTLBに乗せて、
vm_lookup_by_paddr pa 5c000000 -> va c0000000
PTP found. pa=5c000000 va=c0000000
PTE=c0000000
もう一度TLBの乗せます。
(r)0: ac1dcafe
新しく遠いところをマップしてみます。
test4> mem r 70000000                                           
pager_load_page va=70000000 P0
PTP allocated pa=5c004000 va=c0002000
PTE=c0002000
New map pa=5c006000 va=70000000
(r)70000000: 5c00810d
test4> mem r 70000000
(r)70000000: 5c00810d
test4> tlbdump                                                  
MMUCR: 78009081 PTEH: 70000000 PTEL: 5c00610c TTB: 0 TEA: 70000000 PTEA: 70000000
--------------------ITLB--------------------
(-|V)  16M -- WB VPN:89000000 PPN:9000000 PMB [2]
--------------------UTLB--------------------
(D|V) [rwx][---]   8K -- WB -- VPN:c0000000 PPN:5c000000 ASID:0 [16]
(D|V) [rwx][---]   8K -- WB -- VPN:0 PPN:5c002000 ASID:0 [20]
(D|V) [rwx][---]   8K -- WB -- VPN:c0002000 PPN:5c004000 ASID:0 [22]
(D|V) [rwx][---]   8K -- WB -- VPN:70000000 PPN:5c006000 ASID:0 [34]
test4>              


SH4A続き。前回は64MBページ8枚で512MBをマップした。今度はKB単位のページ
でマップしてみよう。

アドレス変換のエリアはU0,P0,P3の3エリアでそれぞれ
  U0 2GB
  P0 2GB
  P3 512MB
だ。なので2GBをマップできる用意をすればいい。
これをページサイズ4KBでマップすると2GB/4KBで524288エントリ必要だ。
それぞれのエントリを32bitで済ませば2MB。64bit使えば4MBがそのために必要。

2GBほとんどを使うアプリケーションでなければ、2GBのうちマップされるのは
数少なくなるため、昔からここはPTEエントリ自体も必要になった時にページを
確保するという戦略になっている。

ページサイズがnKBで、ここに登録されるエントリのサイズがmByteとすると、
一つのページをページテーブルにすると、PTE/PAGEはn * 1024 / m。それぞれの
PTEがnKBをマップするので、一つのページテーブルでn * 1024 * (n * 1024 / m)
をマップできることになる。

これで2GBをマップするには2 * 1024 * m / (n * n)のページテーブルページが
あればいい。

NetBSDではページサイズに4KB、PTEのサイズが4Byteなので、512個。2KBの領域
をスタティックにとっている。

SH4Aの場合、ページサイズを8KBにすればキャッシュエイリアス問題が生じなく
て都合がいい。そしてPTEにはPTELとPTEAの両方を設定できるといいので8Byte
がいいだろう。

ページサイズ8KB PTEサイズ8Bとなると、ページテーブルページのエントリは256。
そのエントリにはPTEが入るので、256*8で2KB必要。

いろいろな例。

これはNetBSD
/*
Page size: 4KB
Page table entry size: 4B
4MB / page table page.
512 page table page entry required. (total 2048byte)
page offset:
....................|||||||||||| 11 10 9 8 7 6 5 4 3 2 1 0 [0x00000fff] 4095
page table page offset. shift=12 mask=0x3ff
..........||||||||||............ 21 20 19 18 17 16 15 14 13 12 [0x003ff000] 4190208
page table page index.  shift=22, mask=0x1ff
.|||||||||...................... 30 29 28 27 26 25 24 23 22 [0x7fc00000] 2143289344
*/
#define	PAGE_SIZE		0x1000
#define	PTE_SIZE		4
#define	PTP_OFFSET		0x3ff000
#define	PTP_OFFSET_SHIFT	12
#define	PTP_OFFSET_MASK		0x3ff
#define	PTP_INDEX		0x7fc00000
#define	PTP_INDEX_SHIFT		22
#define	PTP_INDEX_MASK		0x1ff
#define	PTP_ENTRY		512

これはSH4Aに理想的じゃないかと思っているセッティング。
/*
Page size: 8KB
Page table entry size: 8B
8MB / page table page.
256 page table page entry required. (total 2048byte)
page offset:
...................||||||||||||| 12 11 10 9 8 7 6 5 4 3 2 1 0 [0x00001fff] 8191
page table page offset. shift=13 mask=0x3ff
.........||||||||||............. 22 21 20 19 18 17 16 15 14 13 [0x007fe000] 8380416
page table page index.  shift=23, mask=0xff
.||||||||....................... 30 29 28 27 26 25 24 23 [0x7f800000] 2139095040
*/
#define	PAGE_SIZE		0x2000
#define	PTE_SIZE		8
#define	PTP_OFFSET		0x7fe000
#define	PTP_OFFSET_SHIFT	13
#define	PTP_OFFSET_MASK		0x3ff
#define	PTP_INDEX		0x7f800000
#define	PTP_INDEX_SHIFT		23
#define	PTP_INDEX_MASK		0xff
#define	PTP_ENTRY		256

今日日このくらいの方がいいのでは?という挑戦的なセッティング。
/*
Page size: 64KB
Page table entry size: 8B
512MB / page table page.
4 page table page entry required. (total 32byte)
page offset:
................|||||||||||||||| 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 [0x0000ffff] 65535
page table page offset. shift=16 mask=0x1fff
...|||||||||||||................ 28 27 26 25 24 23 22 21 20 19 18 17 16 [0x1fff0000] 536805376
page table page index.  shift=29, mask=0x3
.||............................. 30 29 [0x60000000] 1610612736
*/
#define	PAGE_SIZE		0x10000
#define	PTE_SIZE		8
#define	PTP_OFFSET		0x1fff0000
#define	PTP_OFFSET_SHIFT	16
#define	PTP_OFFSET_MASK		0x1fff
#define	PTP_INDEX		0x60000000
#define	PTP_INDEX_SHIFT		29
#define	PTP_INDEX_MASK		0x3
#define	PTP_ENTRY		4

この64KBページならむしろ、PTPはスタティックに64KB*4=256KBとってしまって
もいいかも。そしてページテーブルページが絶対メモリの中にあるならかなり
見通しがいい。

これらの#defineはSHIFTとMASKがどうしてもプリプロセッサでできないので、
これらの設定を吐き出すプログラムを先に作りました。

#include <sys/types.h>
#include <stdio.h>

#define	__BEGIN_MACRO	do {
#define	__END_MACRO	} while (/*CONSTCOND*/0)

#define	__bitdisp(a, s, e, m, c)					\
__BEGIN_MACRO								\
	u_int32_t __j, __j1;						\
	int __i, __s, __e, __n;						\
	__n = sizeof(typeof(a)) * NBBY - 1;				\
	__j1 = 1 << __n;						\
	__e = e ? e : __n;						\
	__s = s;							\
	for (__j = __j1, __i = __n; __j > 0; __j >>=1, __i--) {		\
		if (__i > __e || __i < __s) {				\
			printf("%c", a & __j ? '+' : '-');		\
		} else {						\
			printf("%c", a & __j ? '|' : '.');		\
		}							\
	}								\
	if (m) {							\
		printf("[%s]", (char*)m);				\
	}								\
	if (c) {							\
		for (__j = __j1, __i = __n; __j > 0; __j >>=1, __i--) {	\
			if (!(__i > __e || __i < __s) && (a & __j)) {	\
				printf(" %d", __i);			\
			}						\
		}							\
	}								\
	printf(" [0x%08x] %d", a, a);					\
	printf("\n");							\
__END_MACRO
#define	bitdisp(a) __bitdisp((a), 0, 0, 0, 1)

int
main (int argc, const char *argv[])
{
  if (argc < 3)
    return 0;

  uint32_t n = atoi (argv[1]);	// Page size (unit KB)
  uint32_t m = atoi (argv[2]);	// PTE size (unit B)

  printf ("/*\n");
  printf ("Page size: %dKB\nPage table entry size: %dB\n", n, m);
  uint32_t page_table_mapsize = n * n / m;
  uint32_t page_table_page_entry = 2 * 1024 * m / (n * n);
  printf ("%dMB / page table page.\n", page_table_mapsize);
  printf ("%d page table page entry required. (total %dbyte)\n",
	  page_table_page_entry, page_table_page_entry * m);

  printf ("page offset:\n");
  uint32_t PAGE_SIZE = n * 1024;
  uint32_t PTE_SIZE = m;
#define	PAGE_OFFSET	(PAGE_SIZE - 1)
// size of 1 page table page can map.
#define	PTP_MAPSIZE	(PAGE_SIZE / PTE_SIZE * PAGE_SIZE )
#define	PTP_OFFSET	((PTP_MAPSIZE - 1) & ~PAGE_OFFSET)
#define	PTP_INDEX	(~(PTP_MAPSIZE - 1) & ~0x80000000)
  bitdisp (PAGE_OFFSET);
  uint32_t ptp_offset_shift = ffs(PTP_OFFSET) - 1;
  printf ("page table page offset. shift=%d mask=0x%x\n", ptp_offset_shift,
	  PTP_OFFSET >> ptp_offset_shift);
  bitdisp (PTP_OFFSET);
  uint32_t ptp_shift = ffs(PTP_INDEX) - 1;
  printf ("page table page index.  shift=%d, mask=0x%x\n", ptp_shift,
	  PTP_INDEX >> ptp_shift);
  bitdisp (PTP_INDEX);
  printf ("*/\n");

  printf ("#define\tPAGE_SIZE\t\t0x%x\n", PAGE_SIZE);
  printf ("#define\tPTE_SIZE\t\t%d\n", PTE_SIZE);
  printf ("#define\tPTP_OFFSET\t\t0x%x\n", PTP_OFFSET);
  printf ("#define\tPTP_OFFSET_SHIFT\t%d\n", ptp_offset_shift);
  printf ("#define\tPTP_OFFSET_MASK\t\t0x%x\n", PTP_OFFSET >> ptp_offset_shift);
  printf ("#define\tPTP_INDEX\t\t0x%x\n", PTP_INDEX);
  printf ("#define\tPTP_INDEX_SHIFT\t\t%d\n", ptp_shift);
  printf ("#define\tPTP_INDEX_MASK\t\t0x%x\n", PTP_INDEX >> ptp_shift);
  printf ("#define\tPTP_ENTRY\t\t%d\n", page_table_page_entry);

  return 0;
}




岡山のレースは朝からピーカン、風もなく、ドライで予選、決勝走れて最高で
した。予選決勝ともにドライで走れたのは二年ぶり。

土曜は2:30に起きて2:50に出発。やっぱり車の量が多い。給油ランプがついた のと、異様に眠くなってきたので牧之原SAで5:30から6:30まで仮眠。岡崎のあ たりで事故渋滞。亀山Jct-四日市Jctで渋滞ということで、また東名で行く か...と思っていると、東名は事故渋滞が起きてしまったので伊勢湾岸道に。 8時くらいに橋に差しかかった。

しかしこの建造物は凄い。

とにかく橋をかけて継げてしまう強引さがいい。

橋好きにはたまらないな。

東名阪の四日市Jctから亀山Jct.までは渋滞。これだけ絞られたらこれはいつも 渋滞するわけだ。甲南PAでちょっと休憩。コロッケを食べる。9:42

そして名神に入って、ちょっと名神混んでるので京滋バイパス。そして大阪。 大山崎-西宮山口渋滞25km。これがひどかった。止まったらパーキングに入れる くらいのびっちりさ。やっと大阪抜けて赤松PAでちょっと休憩。いつのまに改 装されていた。たこ焼き楽しみにしてたのになくなってしまって残念。ここで もコロッケ。やはり赤松のコロッケはうまいな。
13:25分からのS8の練習走行を見学しようと思っていたのに、美作ICで13:30。 もう間にあわない。料金は2600円。安い。(フルで13300円、深夜割引で7500円)

いつもながらいい風景だ。

サーキット到着は14:00。S8の走行は終わってしまったものの125の走行枠がちょ うどはじまったところだったので、最終の前のコーナーに行って見学。いろん な角度から見ても、今ひとつ走りのイメージはつかめなかったけれど、気合い 感はわかった。





見学し終わったら、うっかり受付時間が終了寸前になっていてあたふたと 受付。そして車検。問題なし。暖機してレーシングして確認してOK。

今回ははじめて西の湯温泉にしてみました。5時頃行ったら先客は一名。出る頃に また一人来たくらいでゆっくり温泉を満喫できました。ほんのちょっと硫黄臭が するのもいいし。(このあたりは温泉で有名だけど、どこもカルキ臭い)
サーキットから一番近いし(といっても15km)、ここはいいかも。

隣の西の屋でてんぷらうどん。まぁおいしくはない。

ビールを1l購入して冷蔵庫に入れてサーキットに帰還。ミニマムのピット混ん でそうだったので、遠慮していたのだけど、また今回もマシンを置かしてもら いました。ありがとうございます。
渋滞疲れで19:30には就寝。夜中になったらとても冷えこんで頭出してると 冷えこんでキーンとしてくる程だった。

朝起きておそるおそる外を見てみると...ドライだ。岡山の場合霧で湿っていた りすると、予選までに乾かない。



予選は8:30分から。ここをこんな感じ?かな〜みたいに走っているとスパスパ 抜かれる。どうも全体的にゆっくり走り過ぎのようだ。
13.0℃ 64.2% 990.2hPa 晴
final 17:35
10分 4周
3l 残 1.8l EVOREX R2 30:1
1:58.397
1:57:278
1:57:953
1:56:769
3.5枚60℃
予選はビリ。
予選終了後、ロガーデータを見てみると...

スピードはきちんととれているけどラップがだめ。ラップがだめな後半でも スピードはまったくOK。これはP-LAPの信号が弱いのかもと、P-LAPの電池を 新品に交換しました。

さて決勝。スタートはミスってしまった。とはいえ順位なりの場所で。モノと S8の3台集団。S8の選手のE/Gは不調で走ってない。バックストレッチでパスす るもブレーキングで前に出られてしまう。そしてまたダブルヘアピンの二個目 で前に出たり、一個目で前に出られたりと、熱く走れた。
どうも僕はリボルバー、パイパーのあたりがまったくだめみたい。どうやって 走っていいのかもやもやしている。
しばらく押さえてた。バックストレッチも100mまで我慢してここでは抜かれな くなった。「100m越えてもいけるで」とは言われたものの、そこまでは無理だっ た。ブレーキ終了時点になると、もうちょっといけたよ!と思うのだけど。
残り二周になって、その苦手なパイパーの立ちあがりで抜かれてしまう。ここ で抜かれてもきっちりつけばストレートで前に出れるな...と思ったものの、離 されてしまい。決勝もビリでした。とはいえ一人旅じゃなかったのでとても 楽しかった。ドライで思いっきり走れたし。
決勝11:25 10周
20℃〜23℃ 43.9% 989.2hPa 晴
final 17:35
11巻き 残10/9
4.3l 残1.4l

3枚68℃

9:55.557
2:31.822
1:53.895
1:53.620
1:54:358
1:54:489
1:53:547
1:53.637
1:53.794
1:53.459
1:53.269
決勝のロガーデータは...

完璧! スピードのノイズの少なさはコンパレータ入れた甲斐があったかな。



1コーナーは消火器越えて70mくらいまでいける。3速落として1コーナー。ふく らみは2/3くらいまで。4速にあげて自然にアウトまで振ったとこからそのまま 全開で2コーナー。ちょっとイン付きが早すぎて開けれないので、アウトに振っ てちょっと待った方がいいかも。フルバンクでチャタがひどい。アトウッド前 はよく覚えてない。70mくらいだったか?3速で進入。進入したらその後全開にし ておけばちょうどいい。ヘアピン前は100m。もっとがんばりたい。2速でヘアピ ン。そのまま吹けきりでリボルバーだとカンカン過ぎ。3速だとエンブレはない し下りだしでよくわからないまま立ちあがりに来てしまう。パイパーもそのま ま3速。思いっきり半クラ。あてるタイミングが遅い。ショートカットの路面も 使って立ちあがり重視にしてもいいのかも。4速まで上げてWヘアピンは50mでブ レーキ。二個目はアウトきっちりでためで入ると立ちあがりが楽。3,4とあげて マイクナイトコーナー。ポストあたりからすっと入ればいい。進入以降はちょっ とアクセル戻してしまう。最終は緑の先まで開けて、一つ落として3速で最終。 4,5と来てブリッジの手前で6速に入るくらい。











正式結果をもらっていこうと時間潰しに、食堂ののカレーを食べたりして待っ ていたのでど、暫定から一時間してもが出てこないので痺れをきらして帰宅。 13:00に出発。


京都までは渋滞なくパス。四日市Jct事故渋滞なので、帰りは名神で。燃料が切れたので多賀SAで給油。SAはごった返して便所も渋滞。
そして彦根あたりから断続渋滞。一宮で渋滞。19時くらいに牧之原SAで休憩なん か食べようかと思うと、レストランも渋滞なので諦めた。
御殿場-大井松田で事故渋滞20km 55分。さらに横浜町田まで渋滞2時間以上に音 を上げ、21:20御殿場で降りた。しかし246も渋滞。結局松田まで70分かかって しまった。そのまま246で帰宅。246走るのは高校の時以来だったのだけど、随 分と整備されたのね。あの頃、作りかけの橋脚を縫うように走ってた記憶があっ たけど、あれが全部完成したんだ。もう20年以上前だもんな。
0:20帰宅。御殿場から3時間かかってしまった。計11時間半弱!
連休と1000円高速のダブルパンチでまいった。

大阪らしい低い雲。なんでこうなるのかググってみたけれどわからなかった。

家-サーキット 645km
サーキット-家 685km
行きは伊勢湾岸道の方から行ったので40kmほど近道。
+西の湯温泉往復 30km
計 1360km
決勝のベストラップ1:53.3のところです。綺麗なデータだ...。走りはともかく。

予選のベストラップ1:56.8のところです。較べてみると、これもいつも通りど こがというよりか全体的にコーナーリングスピードが遅い。ブレーキを追いこ んで、曲がれない〜くらいのとこでコーナーに入ってちょうどいいのかもな。
予選の時なんてコーナー手前でスピード落ちすぎちゃってリリースしてたくら いだ。




やっといいプラグキャップみつけた。30700-KM9-163。ちょっと部番の変更はあ
るけれど、89,90RS125Rのプラグキャップ。このパッケージということは現行だ。



PTELのビットについてまとめておこう。

----------------------------------------------------------------------
 SH4
	 PPN    V  SZ  PR  SZ C  D  SH WT
	[28:10][8][7][6:5][4][3][2][1][0]ハードウェアビット
    [31]				 ソフトウェアビット(NetBSD)
  sw-wired
----------------------------------------------------------------------
 SH4 PCMCIA
 	 PPN    V  SZ  PR  SZ C  D  SH WT
 	[28:10] [8][7][6:5][4][3][2][1][0]ハードウェアビット
    [31]   [11:9]			  ソフトウェアビット(NetBSD)
  sw-wired sw-pcmcia
----------------------------------------------------------------------
 SH4A 32ビットアドレス拡張モード, TLB互換モード。
 	 PPN   UB  V  SZ  PR  SZ C  D  SH WT
	[31:10][9][8][7][6:5][4][3][2][1][0]
UBビットは29ビットアドレスモードではPASCR[6:0]で指定されていたものが
ページ毎の設定となる。
----------------------------------------------------------------------
 SH4A 32ビットアドレス拡張モード、TLB拡張モード
 	 PPN   UB  V  C  D  SH WT
	[31:10][9][8][3][2][1][0]	ページサイズ、保護はPTEAで設定。

 SH4A 32ビットアドレス拡張モード, TLB互換モードにすると、リザーブのビッ
トは一切ない。

オリジナルのsh3のpmapの実装ではソフトウェアビットは全てvm_page扱いにし
てたのだけど...。大体こんな感じでいずれ破綻するので基本的にはリザーブの
ビットは使わない方がいいんだ。

色々考えてみよう。OSのデフォルトページャは固定したページサイズでいいだ
ろう。そのページャの扱うページは全て同じサイズを強制することで見通しが
よくなる。

それ以外のサイズのページが欲しい時がある。例えばフレームバッファをアド
レス変換して登録したいとか。この時、KB単位のページでマップしていたらキ
リがない。そして、画面表示のためだけにTLBが埋めつくされてしまう。

こういう時はワイヤードエントリで直接TLBに登録して、ページャの管轄外にし
よう。デバイスのページならば共通ページでいいし、ワイヤードであるのも好
ましい。しかしユーザ空間へのマップは無理だ。これは仕方ない。

キャッシュについても、ページャ管轄のページはキャッシュするか、しないか
の二択で、ページごとにライトスルーかライトバックを指定することもない。

となると、ページサイズとライトスルーの設定のビットは、ページャ管轄のペー
ジに限っては固定になる。

乗り気はしないけれど、押し込むことは可能か。個人的にはページテーブルエ
ントリはptel用とptea用をそれぞれ別にとって計64bitにするのがいいと思うけれど。

ちゅっと寝て、岡山に出発です。豊田Jct.で伊勢湾岸道。四日市Jct.で亀山の 方に。亀山Jct.で新名神だな。カーナビはないので、きっちり覚えておかないと。



台風でガレージがちょっと浸水してしまった。今年は去年のようなゲリラ豪雨
もなく安泰だったのだけれど、ここにきて。ここずっとの雨も相まって、機械
に錆が...。必死にWD-40かけながら錆落としして、油引き。



SH4A続き。 TLBにエントリに乗せるにはPTEH,PTEL,PTEA(TLB拡張モードのみ)に値を設定し てLDTLBする。PTEHは仮想アドレスとアドレススペースID。PTELに入れるのは物 理アドレスの他、色々な(キャッシュ、ページサイズ、プロテクト...)情報だ。 PTEAはPTELに収まりきれなかったものを設定するためにある。
OS側では仮想アドレスをインデックスとして、それに対するページ情報をスト アしている。なのでそのインデックスでPTEHは設定できる。ASID(アドレススペー スID)は、その状況でのアドレス空間の違いを示すものなので、その場での設定 でいい。
ページの設定はその仮想アドレスでインデックスされるものの中に設定されて いる。それをPTEL,PTEAに設定してTLBにロードする。ここでPTEA(Page Table Entry Assistance Register)が必要なのは、PTELだけでは必要な情報をロード できないからだ。
素直に考えると、仮想アドレスでインデックスされるのは
struct
{
  uint32_t ptel;
  uint32_t ptea;
}
だ。しかし、こうするにはかなりの度胸がいる。できれば32bitに押しこめたい。
そこで、ハードウェアでは使っていないビットに、種々の情報を押しこめて、 実際にロードする段階でデコードしようという手段になる。
そもそも本当に押しこめるならハードウェアの設計時点で押しこめている。押 しこめる場所としてはハードウェアでリザーブになっている場所だ。そこでも 押しこめなくなると、ソフトウェア的な妥協を入れることになる。
NetBSDの場合、SH4のPCMCIA空間の設定にPTEAを使う。この設定は[11:9]に入れ ている。これはNetBSDが4Kページだということを前提にしてPPNを潰して [11:9]に入れている(4Kページなら[11:10]は使われないビットだからだ)。[9] はリザーブ。
そしてここで今回のSH4AのTLB拡張モードだ。これはPTEAに10bit使う。どうあ がいても押し込めない。
さらにNetBSDではソフトウェア的なワイヤードページにPTELの31bit目を使って いるのだけど、これはSH4までの29bit物理アドレスならいいけれど、32bitアド レスモードでは、かちあってしまっていけない。
物理アドレスで2G以上の空間にあるメモリをサポートしないなら、このままで もOKか。
そうなるとソフトウェアの事情でPPNのマスクを変更しないといけない。うーん...


ロガー計画続き。スピードセンサもコンパレータ経由にしてみました。3.3Vから
10kΩでプルアップして、それをLM339に。P-LAPと同じくアクティブロー。
LM339の出力はオープンドレインなので、ここはLPC2388の内部でプルアップ。
LM339は5Vで駆動。これはP-LAPが5Vのため。

VccをA/Dコンバータでとろうかとか思っていたけれど、よくよく考えてみたら Vccが落ちてきたらVrefも落ちるのでだめだ。
そしてテスタをあてていてとんでもないことが発覚。なんと今迄、本来3.3V駆 動のLPC2388を5Vで駆動していた。SDカードも5Vで動かしていた。
というのも、Interface 2009/05号 p41のピン配置表が間違っていて、CN2の3.3Vと 5Vが反対なんだ。本当はCN2.38が5V、CN2.39が3.3V。あとCN3も間違えていて、 CN3.A19がP0_21でCN3.B19がP0_18。
この右側のロガー試作テスト機は、これに気付いていて、正しい結線にしてい たのだけど、ロガー本体は気付く前に電源廻りを組んでいて、そのままだっ た...。
今迄動いてたのが凄い。LPC2388もSDカードも屈強だ。
そうそう、P-LAP3も1点計測に。いちいちこれ変更するの面倒だ。もうちょっと 安定すればP-LAP本体は外してロガーから全て駆動できるのだけど...。

SH4A続き。PMBのエントリはITLBだけに登録される。UTLBには乗らない。ITLBに
登録されるのは最大2つまで。これはマニュアルにはなく、観測結果。

まずは512MBをMMUにP0にマップします。SH7785のTLB拡張モードを使って64Mペー
ジで8枚マップしてみます。

uint32_t
mmu (int32_t argc __attribute__((unused)),
     const char *argv[] __attribute__((unused)))
{
  struct tlb_entry tlb_entry =
    { 0x00000000, 0x40000000, 0, WRITEBACK, PG_64M, P_RW, FALSE };

全部のTLBエントリをゼロクリアします。明示的にしない限り、有効でないなん
らかの値が入っています。

  sh4a_tlb_clear_all ();

SH4A TLB拡張モードでアドレス変換スタート。
  sh4a_mmu_start ();

512MBのメモリを全てP0にマップします。64MBページなので8ページだけ。
  int i;
  for (i = 0; i < 8; i++)
    {
      sh4a_tlb_entry_generate (&tlb_entry);
      tlb_entry.vpn += 0x4000000;
      tlb_entry.ppn += 0x4000000;
    }

  return 0;
}


void
sh4a_mmu_start ()
{
ASIDを設定します。これはTLBエントリのマッチに使われます。
  // Set ASID
  *SH4A_PTEH = 0;
エントリ全部無効化して、アドレス変換を開始します。
  // Invalidate all entries. and start Address translation.
  *SH4A_MMUCR = MMUCR_TI | MMUCR_AT;
#ifdef SH4A_EXT_MMU
  *SH4A_MMUCR |= MMUCR_ME; ここでTLB拡張モードに設定します。
#endif

  CPU_SYNC_RESOURCE ();
}

void
sh4a_tlb_entry_generate (struct tlb_entry *e)
{
  uint32_t pteh, ptel, ptea;
  uint32_t pgsz_table[] = { 0x400, 0x1000, 0x1000, 0x100000, 0x2000, 0x40000,
			    0x400000, 0x4000000 };
  uint32_t pgsz = pgsz_table[e->size];
  uint32_t pgsz_mask = pgsz - 1;

  if ((e->vpn & pgsz_mask) || (e->ppn & pgsz_mask))
    {
      iprintf ("*** miss aligned address *** %x %x %x\n", e->vpn, e->ppn,
	       pgsz_mask);
      return;
    }
  if (e->asid & ~PTEH_ASID)
    {
      iprintf ("*** invalid ASID %x\n", e->asid);
      return;
    }

  pteh = e->vpn | e->asid;
  ptel = e->ppn;
  if (e->shared)
    ptel |= PTEL_SH;
  switch (e->cache)
    {
    case UNCACHED:
      break;
    case WRITEBACK:
      ptel |= PTEL_C;
      break;
    case WRITETHRU:
      ptel |= (PTEL_C | PTEL_WT);
      break;
    }
#ifdef SH4A_EXT_MMU
  uint32_t extmmu_pgsz_bits[] = { PTEA_ESZ_1K, PTEA_ESZ_4K, PTEA_ESZ_64K,
				  PTEA_ESZ_1M, PTEA_ESZ_8K, PTEA_ESZ_256K,
				  PTEA_ESZ_4M, PTEA_ESZ_64M };
  ptea = extmmu_pgsz_bits[e->size];
  switch (e->protect)
    {
    case P_RDONLY:
      ptea |= PTEA_EPR_P_R | PTEA_EPR_P_X;
      break;
    case P_RW:
      ptea |= PTEA_EPR_P_R | PTEA_EPR_P_W | PTEA_EPR_P_X;
      break;
    case U_RDONLY:
      ptea |= PTEA_EPR_P_R | PTEA_EPR_P_W | PTEA_EPR_P_X |
	PTEA_EPR_U_R | PTEA_EPR_U_X;
      break;
    case U_RW:
      ptea |= PTEA_EPR_P_R | PTEA_EPR_P_W | PTEA_EPR_P_X |
	PTEA_EPR_U_R | PTEA_EPR_U_W | PTEA_EPR_U_X;
      break;
    }
  *SH4A_PTEA = ptea;
#else
#error "notyet"
#endif
  *SH4A_PTEH = pteh;

 ここでPTEL_Vを設定しなければTLBミス例外が、PTEL_Vを設定してPTEL_Dを設
定しなければTLB初期ページ例外が発生します。

  *SH4A_PTEL = ptel | PTEL_V | PTEL_D;

登録するエントリを一つづつずらします。
  uint32_t r = *SH4A_MMUCR;
  uint32_t urc = (r >> MMUCR_URC_SHIFT) & MMUCR_URC_MASK;
  urc++;
  urc &= MMUCR_URC_MASK;
  *SH4A_MMUCR &= ~(MMUCR_URC_MASK << MMUCR_URC_SHIFT);
  *SH4A_MMUCR |= urc << MMUCR_URC_SHIFT;
ここで登録。
  __asm volatile ("ldtlb");
  CPU_SYNC_RESOURCE ();
}



=> go 0x89000000
## Starting application at 0x89000000 ...
stack_start: 0x89100000
RAM data: 0x890078a4-0x89007df4 1360byte
bss: 0x89007e00-0x8900bb8c 15756byte
Privilege-mode, bank 1, Exception disabled, FPU enabled, IMASK=0xf
bss memory check passed.
PMB page size 16MB mask=ffffff VPN:a4000000 PPN:4000000
PMB page size 128MB mask=7ffffff VPN:90000000 PPN:58000000
md_thread_create: [2]:uart recv pc=890048c0 sp=8900b658 stack=1024byte
thread_start: [2]
md_thread_create: [3]:uart send pc=89004850 sp=8900bb30 stack=1024byte
thread_start: [3]
md_thread_create: [4]:test pc=8900194c sp=8900a95c stack=1024byte
thread_start: [4]
test_thread:1234abcd
INTC: fffffff6 1f000000
hello 0
test4> mmu
test4> tlbdump
MMUCR: 2c002081
PTEH: 1c000000
PTEL: 5c00010c
TTB: 0
TEA: 0
PTEA: 0
--------------------ITLB--------------------
(-|V)  16M -- WT VPN:89000000 PPN:9000000 PMB 
--------------------UTLB--------------------
(D|V) [rwx][---]  64M -- WB -- VPN:0 PPN:40000000 ASID:0 
(D|V) [rwx][---]  64M -- WB -- VPN:4000000 PPN:44000000 ASID:0 
(D|V) [rwx][---]  64M -- WB -- VPN:8000000 PPN:48000000 ASID:0 
(D|V) [rwx][---]  64M -- WB -- VPN:c000000 PPN:4c000000 ASID:0 
(D|V) [rwx][---]  64M -- WB -- VPN:10000000 PPN:50000000 ASID:0 
(D|V) [rwx][---]  64M -- WB -- VPN:14000000 PPN:54000000 ASID:0 
(D|V) [rwx][---]  64M -- WB -- VPN:18000000 PPN:58000000 ASID:0 
(D|V) [rwx][---]  64M -- WB -- VPN:1c000000 PPN:5c000000 ASID:0 

TLB拡張モードでは、DA2をゼロクリアしてもプロテクションモードの特権の
r-xは消えません。特権モードでも読めない、フェッチできないエントリがあっ
ても意味がない。

このプログラムが動いている0x8000000-0xc000000以外をゼロクリアします。
test4> mem c 14000000 4000000
clear 14000000-18000000
test4> mem c 18000000 4000000
clear 18000000-1c000000
test4> mem c 1c000000 4000000
clear 1c000000-20000000
test4> mem c 10000000 4000000
clear 10000000-14000000
test4> mem c c000000 4000000
clear c000000-10000000
test4> mem c 4000000 4000000
clear 4000000-8000000
test4> mem c 0 4000000
clear 0-4000000
ここでまたパターン探しします。
test4> mem r 89000000
(r)89000000: 402bd001
test4> mem f 402bd001
Find pattern 402bd001... (0-20000000)
402bd001 found at 9000000
402bd001 found at 90083a8
402bd001 found at 90083dc

0x8000000-0xc000000以外の領域からは見つからなかったことからシャドウはない。

test4> mem r 89000004
(r)89000004: 90009




ちょっと気になっていたところを直した。ここ、ラジエターホースがハイテン
ションコードと干渉している。ここでE/Gの振動が不用意にプラグに伝わって
キャップが摩耗してノイズが飛ぶのかも..と思い



取り廻しを変更しました。

ブレーキフルードを交換して、火入れの確認してOK。今回から水を出す時は ウォーターポンプのドレンからも抜いておくことにした。あそこにたまってい ると腐食して消耗の原因になるかもしれない。ウォーターポンプのシールはで きる限り交換したくない。ちょっと今のE/Gの廻りに不満なんだ。

岡山のS8は指定ガソリンじゃないといけないので、余ってる筑波ガソリンを予 備容器に移してカラッポに。ここずっと倉庫となっていた後部座席を掃除。車 中泊の準備。筑波まで1時間20分。富士まで1時間30分故、車中泊は岡山だけな のだ。
冷蔵庫、ポット、エアマット、電気毛布、シュラフ。寝間着。もりあがってきた。
今週、東名の集中工事なのだけど、連休中は金曜の18時から解除だから多分大 丈夫でしょう。今回はちゃんと伊勢湾岸道で行こう。豊田Jctで入って、四日市 Jct.で鈴鹿、亀山の方に行って、亀山Jct.で新名神か。不安だ。
はじめて筑波に行った時(19年前、ちょうどCXができた後だ)首都高を抜ける自 信がなくて延々と環七廻って加平から入ったくらいのへたれだからね...。当時 の箱崎は3車線くらいまたがないといけなくて大変だったんだよ。
帰りの予測を調べると大和TNで1時間渋滞っぽい。京都あたりでぶらぶらしてく かな。

SH4A続き。
strtollを使うようになると(newlib)
	.data :
	{
		_data_start = .;
		*(.eh_fram*)
		*(.data)
		 . = ALIGN (4);
	} > ram
	_data_end = .;
eh_frameが必要になる。strtolなら不用。なんなのか不明。


32bitアドレス拡張モードで自分が走ってるリージョンを変更してみます。変更
途中にメモリをアクセスするとリセットしてしまうのでここはアセンブラで確
実にレジスタだけで更新できるようにします。P1とP2を一気に変更。

// void sh4a_pmb_entry_set_self (uint32_t aa0, uint32_t da0, uint32_t aa1,
//	uint32_t da1)
FUNC (_sh4a_pmb_entry_set_self)
	mov.l	r8,	@-r15
	mov.l	r9,	@-r15
	mov.l	r10,	@-r15
	mov.l	.L_MMUCR,	r8
	mov	#4,		r9	// MMUCR.TI
	mov.l	.L_PMB_AA0,	r0
	mov.l	.L_PMB_DA0,	r1
	mov.l	.L_PMB_AA1,	r2
	mov.l	.L_PMB_DA1,	r3
	mov.l	r4,	@r0
	mov.l	r5,	@r1
	mov.l	r6,	@r2
	mov.l	r7,	@r3
	// MMUCR.TI = 1
	mov.l	@r8,	r10
	or	r9,	r10
	mov.l	r10,	@r8
	// Sync resrouce.
	icbi	@r0
	mov.l	@r15+,	r10
	mov.l	@r15+,	r9
	rts
	 mov.l	@r15+,	r8
	.align	2
.L_MMUCR:
	.long	0xff000010
.L_PMB_AA0:
	.long	0xf6100000
.L_PMB_AA1:
	.long	0xf6100100
.L_PMB_DA0:
	.long	0xf7100000
.L_PMB_DA1:
	.long	0xf7100100


void
pmb_setup ()
{
  struct
  {
    uint32_t aa;
    uint32_t da;
  } pmb [] = {
このOSが走っている16MBの部分だけを登録しなおします。
    // P1
    { 0x89000000 | PMB_AA_V,
      0x09000000 | PMB_DA_V | PMB_DA_C | PMB_DA_SZ_16M },
    // P2
    { 0xa9000000 | PMB_AA_V,
      0x09000000 | PMB_DA_V | PMB_DA_SZ_16M },
PLDのレジスタ領域もマップし直します。
    // PLD
    { 0xa4000000 | PMB_AA_V,
      0x04000000 | PMB_DA_V | PMB_DA_SZ_16M },
ここは32bitモードでDDR2が512MBマップされているエリア。一気にマップできないので
128MBごとにマップしてテスト。
    // RAM
    //    { 0x90000000 | PMB_AA_V,
    //      0x40000000 | PMB_AA_V | PMB_DA_C | PMB_DA_SZ_128M },
全領域のメモリテストでリセットしたところから、ここがエリア2,3
(0x08000000-0x0fffffff)にマップされているのではないかと仮定します
          { 0x90000000 | PMB_AA_V,
      0x48000000 | PMB_AA_V | PMB_DA_C | PMB_DA_SZ_128M },//memtest failed.
    ///   { 0x90000000 | PMB_AA_V,
    //      0x50000000 | PMB_AA_V | PMB_DA_C | PMB_DA_SZ_128M },
    //    { 0x90000000 | PMB_AA_V,
    //      0x58000000 | PMB_AA_V | PMB_DA_C | PMB_DA_SZ_128M },
  };
ここはクリティカルなので前述のアセンブラルーチンで更新。
  // Change PMB setting P1/P2
  sh4a_pmb_entry_set_self (pmb[0].aa, pmb[0].da, pmb[1].aa, pmb[1].da);

ここからはアクセスされない限り何も起こらないので、Cルーチンで更新。
  // Additional PMB.
  size_t i;
  for (i = 2; i < sizeof pmb / sizeof (pmb[0]); i++)
    {
ここでページサイズとVPN,PPNが合致しているかどうかチェック。C
合致していないまま動かせばリセットです。
      if (!sh4a_pmb_align_check (pmb[i].aa, pmb[i].da))
	continue;
      sh4a_pmb_entry_set (i, pmb[i].aa, pmb[i].da);
      *SH4A_MMUCR |= MMUCR_TI;	// Invalidate TLB
      CPU_SYNC_RESOURCE ();	// For MMUCR
    }
}

これで実行。

=> go 0x89000000
## Starting application at 0x89000000 ...
stack_start: 0x89010000
RAM data: 0x89007144-0x8900767c 1336byte
bss: 0x89007680-0x8900b40c 15756byte
Privilege-mode, bank 1, Exception disabled, FPU enabled, IMASK=0xf
bss memory check passed.
PMB page size 16MB mask=ffffff VPN:a4000000 PPN:4000000
PMB page size 128MB mask=7ffffff VPN:90000000 PPN:48000000
md_thread_create: [2]:uart recv pc=890047b4 sp=8900aed8 stack=1024byte
thread_start: [2]
md_thread_create: [3]:uart send pc=89004744 sp=8900b3b0 stack=1024byte
thread_start: [3]
md_thread_create: [4]:test pc=89001854 sp=8900a1dc stack=1024byte
thread_start: [4]
test_thread:1234abcd
INTC: fffffff6 1f000000
hello 0
test4> pmb
--------------------PMB--------------------
paddr 32bit mode.
VPN:89000000 (V) PPN:9000000 (V) 16MB, bufferd write, cacheable, write-back, 
VPN:a9000000 (V) PPN:9000000 (V) 16MB, bufferd write, uncacheable, write-back, 
VPN:a4000000 (V) PPN:4000000 (V) 16MB, bufferd write, uncacheable, write-back, 
これはDDR2エリア512MBのうち、128MB目からの128MB。
VPN:90000000 (V) PPN:48000000 (V) 128MB, bufferd write, cacheable, write-back, 
--------------------ITLB--------------------
VPN:89000000 ASID:0 (-) PPN:9000000 (-) PMB 16MB U cacheable, shared
VPN:89000000 ASID:0 (-) PPN:9000000 (-) PMB 16MB U cacheable, shared
VPN:a9000000 ASID:0 (V) PPN:9000000 (V) PMB 16MB U uncacheable, shared
VPN:89000000 ASID:0 (V) PPN:9000000 (V) PMB 16MB U cacheable, shared
現在のOSの原点の値を取ります。
test4> mem r 89000000
(r)89000000: 402bd001
この0x402bd001がある場所を検索します。
test4> mem f 0 402bd001                                         
Find pattern 402bd001...
402bd001 found at 91000000
0x91000000に見つかったので確かめます。
test4> mem r 91000000
(r)91000000: 402bd001
ここに0xdeadbeafを書き込みます。
test4> mem w 91000000 deadbeaf                                  
(w)91000000: deadbeaf
書き込んだのを確認します。
test4> mem r 91000000
(r)91000000: deadbeaf
0x89000000の値を読んでみます。
test4> mem r 89000000
(r)89000000: 402bd001
キャッシュフラッシュしてみます。
test4> cacheflush
ここに書かれていました。 ここでキャッシュフラッシュしないと値が見えない
のは本当の物理的には同じメモリだけれど、CPU的には別の物理アドレスなので、
キャッシュシステムにおけるPPNが違うのでマッチしない。
test4> mem r 89000000                                           
(r)89000000: deadbeaf
test4> 

ということで、物理アドレスの0x4000000から512MBマップされているRAMのうち、
128MB-256MBが物理アドレス0x08000000-0x0fffffffにもマップされている様子。



CR85の来週の岡山に向けて整備は大体終了。ゼッケンも40に。40はいいな。ファ
イナルはドライで17:35で。6 速を0.96から0.92にして以来、筑波富士岡山でい
ろいろ試してきたけれど、結局0.96の時と同じじゃないとインフィールドがど
こもだめだ。


台風一過で、今迄になく絶好のコンディションで走れるのではないかと期待。 なんとしてでも体調を整えねば。
SH4A続き。SH4Aの32bitアドレス拡張モードを試した。R0P7785LC0011RLでは S2-5のDIPスイッチで32bitブートができる。
マニュアルではDDR2エリアは29bitモードで(0x08000000-0x0fffffff)、 32bitモードで(0x40000000-0x5fffffff)になるとなっている。しかしu-bootを 立ちあげると0x0800000をRAMとして使っていて、それでプログラムもロードし 実行できてしまう。ここのエリアは32bitモード時はUSBエリアとSDエリアなのに。
u-bootのソースを見るとu-boot/board/sh7785lcr/lowlevel_init.SでPASCRを見 て32bitモードならUSBとSDエリアのマッピングを変更しているけれど、それ以 外の部分ではまったく32bitモードへの対応がない。
それでも動いているということは、マニュアルとハードウェアの仕様が違うの だろう。
32bitモードでブートした後のPMBの様子を見てみました。
=> go 0x89000000
## Starting application at 0x89000000 ...
stack_start: 0x89010000
RAM data: 0x89006234-0x890066c0 1164byte
bss: 0x890066c0-0x8900a31c 15452byte
Privilege-mode, bank 1, Exception disabled, FPU enabled, IMASK=0xf
bss memory check passed.
--------------------PMB--------------------
paddr 32bit mode. これはPASCRの31ビットを見ています。
16エントリのPMBを全てダンプ
VPN:80000000 (V) PPN:0 (V) 512MB, bufferd write, cacheable, write-thru, 
VPN:a0000000 (V) PPN:0 (V) 512MB, bufferd write, uncacheable, write-back, 
P0,P1にあたる部分があらかじめ登録されている。(これはCPUがやってくれる)
P0相当部分がデフォルトではライトスルーなのに注意。32bitモードになると
CCR.WTは無効になり、キャッシュの設定はPMBのエントリで設定する。
ここでライトスルーをライトバックに変更してみた。

VPN:8f000000 (-) PPN:35000000 (-) 512MB, bufferd write, cacheable, write-back, 
VPN:aa000000 (-) PPN:9000000 (-) 16MB, unbufferd write, uncacheable, write-thru, 
VPN:ae000000 (-) PPN:9d000000 (-) 64MB, bufferd write, uncacheable, write-back, 
VPN:bf000000 (-) PPN:1000000 (-) 64MB, unbufferd write, cacheable, write-thru, 
VPN:a7000000 (-) PPN:31000000 (-) 64MB, unbufferd write, uncacheable, write-back, 
VPN:8e000000 (-) PPN:2b000000 (-) 64MB, unbufferd write, uncacheable, write-thru, 
VPN:aa000000 (-) PPN:12000000 (-) 64MB, unbufferd write, uncacheable, write-back, 
VPN:8e000000 (-) PPN:11000000 (-) 64MB, unbufferd write, uncacheable, write-thru, 
VPN:8f000000 (-) PPN:11000000 (-) 64MB, unbufferd write, cacheable, write-thru, 
VPN:8b000000 (-) PPN:b5000000 (-) 64MB, bufferd write, uncacheable, write-thru, 
VPN:a7000000 (-) PPN:75000000 (-) 64MB, unbufferd write, uncacheable, write-thru, 
VPN:9f000000 (-) PPN:32000000 (-) 64MB, unbufferd write, uncacheable, write-thru, 
VPN:85000000 (-) PPN:d1000000 (-) 64MB, unbufferd write, uncacheable, write-thru, 
VPN:8f000000 (-) PPN:11000000 (-) 64MB, unbufferd write, uncacheable, write-thru, 
cache CCR=101
md_thread_create: [2]:uart recv pc=89004610 sp=89009de8 stack=1024byte
thread_start: [2]
md_thread_create: [3]:uart send pc=890045a0 sp=8900a2c0 stack=1024byte
thread_start: [3]
md_thread_create: [4]:test pc=89001788 sp=8900911c stack=1024byte
thread_start: [4]
test_thread:1234abcd
INTC: fffffff6 1f000000
hello 0
test4> 
test4> test
--------------------PMB--------------------
paddr 32bit mode.
VPN:80000000 (V) PPN:0 (V) 512MB, bufferd write, cacheable, write-back, 
ライトバックに変更されている。キャッシュテストしてみて動作も確認しました。
VPN:a0000000 (V) PPN:0 (V) 512MB, bufferd write, uncacheable, write-back, 
VPN:ad000000 (-) PPN:35000000 (-) 512MB, bufferd write, cacheable, write-back, 
VPN:a8000000 (-) PPN:21000000 (-) 16MB, unbufferd write, uncacheable, write-thru, 
VPN:ae000000 (-) PPN:9d000000 (-) 64MB, bufferd write, uncacheable, write-back, 
VPN:bd000000 (-) PPN:1000000 (-) 512MB, bufferd write, cacheable, write-thru, 
VPN:a5000000 (-) PPN:33000000 (-) 64MB, unbufferd write, uncacheable, write-back, 
VPN:8c000000 (-) PPN:2b000000 (-) 64MB, unbufferd write, uncacheable, write-thru, 
VPN:a8000000 (-) PPN:52000000 (-) 64MB, unbufferd write, cacheable, write-back, 
VPN:8c000000 (-) PPN:11000000 (-) 64MB, unbufferd write, uncacheable, write-thru, 
VPN:8d000000 (-) PPN:37000000 (-) 64MB, unbufferd write, cacheable, write-thru, 
VPN:89000000 (-) PPN:bd000000 (-) 64MB, bufferd write, uncacheable, write-thru, 
VPN:a7000000 (-) PPN:75000000 (-) 64MB, unbufferd write, uncacheable, write-thru, 
VPN:9d000000 (-) PPN:3e000000 (-) 64MB, unbufferd write, uncacheable, write-thru, 
VPN:85000000 (-) PPN:d1000000 (-) 16MB, unbufferd write, uncacheable, write-thru, 
VPN:8f000000 (-) PPN:11000000 (-) 64MB, unbufferd write, uncacheable, write-thru, 
--------------------ITLB--------------------
VPN:a0000000 ASID:0 (V) PPN:0 (V) PMB 512MB U uncacheable, shared
VPN:80000000 ASID:0 (V) PPN:0 (V) PMB 512MB U cacheable, shared
VPN:80000000 ASID:0 (-) PPN:0 (-) PMB 512MB U cacheable, shared
VPN:a0000000 ASID:0 (-) PPN:0 (-) PMB 512MB U uncacheable, shared
ITLBにもP0,P1相当のPMBが乗っている。

これをそのまま29bitモードで動かすと、
test4> test
--------------------PMB--------------------
paddr 29bit mode.これはPASCRの31ビットを見ています。
Buffered write setting: |--------| x:unbuffered write.
VPN:a1000000 (-) PPN:10000000 (-) 512MB, unbufferd write, cacheable, write-back, 
VPN:8d000000 (-) PPN:0 (-) 64MB, unbufferd write, uncacheable, write-thru, 
VPN:af000000 (-) PPN:35000000 (-) 512MB, bufferd write, cacheable, write-back, 
VPN:aa000000 (-) PPN:21000000 (-) 16MB, unbufferd write, uncacheable, write-thru, 
VPN:ae000000 (-) PPN:9d000000 (-) 64MB, bufferd write, uncacheable, write-back, 
VPN:bf000000 (-) PPN:1000000 (-) 512MB, bufferd write, cacheable, write-thru, 
VPN:a7000000 (-) PPN:33000000 (-) 64MB, unbufferd write, uncacheable, write-back, 
VPN:8e000000 (-) PPN:2b000000 (-) 64MB, bufferd write, uncacheable, write-thru, 
VPN:8a000000 (-) PPN:52000000 (-) 64MB, unbufferd write, uncacheable, write-back, 
VPN:8e000000 (-) PPN:11000000 (-) 64MB, unbufferd write, uncacheable, write-thru, 
VPN:8f000000 (-) PPN:11000000 (-) 64MB, unbufferd write, cacheable, write-thru, 
VPN:8b000000 (-) PPN:bd000000 (-) 64MB, bufferd write, uncacheable, write-thru, 
VPN:a7000000 (-) PPN:75000000 (-) 64MB, bufferd write, uncacheable, write-thru, 
VPN:9f000000 (-) PPN:3e000000 (-) 64MB, unbufferd write, uncacheable, write-thru, 
VPN:85000000 (-) PPN:d1000000 (-) 16MB, unbufferd write, uncacheable, write-thru, 
VPN:8f000000 (-) PPN:11000000 (-) 64MB, bufferd write, uncacheable, write-thru, 
--------------------ITLB--------------------
VPN:90e22c00 ASID:33 (-) PPN:d8aef800 (-) PMB 512MB U uncacheable, shared
VPN:d2a60800 ASID:b1 (-) PPN:e06df400 (-) 64K U cacheable, shared
VPN:acb79400 ASID:21 (-) PPN:d7be2000 (-) PMB 128MB U uncacheable, shared
VPN:e086ac00 ASID:25 (-) PPN:e22f6800 (-) 1K U uncacheable, shared

いろいろいじっていて気付いたのだけど、ライトスルーの時はライトアロケー
トじゃない(ライトアロケート:書き込み時の該当ラインをキャッシュに乗せる
動作。ライトバックでライトアロケート、ライトスルーはライトアロケートし
ないのが普通)。忘れてた。



来週の岡山に向けて整備。まずはカウル、ラジエター、ホイール、チェーン洗
い。どうしてもレインが必要だったら岡山のサービスでいれる。朝のウェット
を走ればいいだけなら(岡山によくある状況)、去年のレインにしよう。となる
と3セットのホイールを持っていかないといけないか...。



フォークオイルは真っ黒。富士の前に替えたばっかりなのに。例によって左側 はアルミ粉がでている。なんとかしないととは思っているものの...。

NFはこのリア周りが惚れ惚れする。

ピストンはまったく無事だった。よかった。このピストンで岡山までいける。
昨日はレインファイナルで、バックストレッチ50m看板で吹けきってしまい、そ こから先、吹け切りのまま全開だったので、走行中から気が気でなかった。
最近のオイルはよく燃えるから濃くてもいい。と聞いて、決勝からEVOLEX R2を 今迄の40:1から30:1にしたのもよかったかも。





筑波4戦でした。晴れたり降ったりまた晴れたりで、とてつもなくしんどかった
です。結果は予選20位、決勝17位とだめでした。


4時半に起きて出発、柏のあたり結構青空が拡がっていい感じ...6時にサーキッ トに着くとドンヨリ。でもこれは期待できる。ということでスリックに交換。
予選は8:40から。ダンロップの舗装が改修になって期待していたのだけど、水 たまりができなくなったくらいで、やっぱりあそこが一番水の引きが悪い。
どこまでいけるのかわからず、今回、かなり抜かれるチャンスがあって、お手 本はあったのだけど、どうしても怖さが先だってしまい、予選は下から3番目。
予選が終了するとピーカンに晴れてきて、決勝は楽しめそうだな〜と思ってい た。しかし、真っ黒い雲がやってきて本振りの雨。西の空もどんよりで回復が 見込めそうもない。
12:30からスタート前チェック。11:30分くらいではまだパラパラ降っていた。 空を見ても回復しそうにないのでレインに。
駐車場で一応気休めにウォーマーをかけておく。10分前に出発すれば余裕で コースインゲートかな。
そしてコースイン。すると...ピーカンに晴れた。そしてサイティングラップに 入ると...うわ。ほとんどドライだ。ここでタイヤ交換した人もいたけれど、僕 は交換できる体制じゃないので、そのまま。うっかりラジエターのガムテーム がフルウェット仕様のままだったことに選手紹介の時に気付いて慌てて剥した。 あぶなかった。
スタートは普通に。一周目はちょっと気合い負けしてしまった。ファイナルも フルウェット仕様の15:35だ。バックストレッチで吹けきってしまう。それでも 序盤、わいわいとつつき合って楽しんでいたのだけど、途中からちょっと、タ イヤがおかしい。7周目でタイヤが終わってしまった。
このタイヤであと8周も走るの???とゾッとするレベル。周回ごとにひどくなっ ていき、突っこみは十分いけるのだけど、開けはじめるとズルズル。じんわー り開けながら辛抱の8周でした。最終ラップの最終コーナーで一台抜かれてしまっ た。

リアタイヤ、ブロックが消えてるし。

フロントは走ってる時も不安を感じなかっただけあって、まぁそこそこもって る。

今シーズンの筑波選手権は本当に天候にふりまわされた。

ロガーデータ。これは予選。ちょっとスピードメータのノイズ乗りが気になる けれど、十分いい。スピードメータもコンパレータかますかな。
最終進入が161km/h程度なのでちょっと調子がよくない。

決勝までにロガーの充電はしておいたのだけど、決勝のデータはボロボロ。序 盤スピードメータにノイズが乗りまくってもラップはとれてるので、これはコ ンパレータを入れた効果だろう。しかしこのノイズは..車体まわり?うーん。難 しい。
電源状況の確認にLPC2388のVccをA/Dコンバータでサンプリングしてみようか。 なかなか先が見えない。



朝起きてみると、ちょっと具合悪いけれど、熱はないので、特走行ってきまし
た。6時半過ぎに着いた頃にはまだドライだったのだけど、段々霧雨が強くなっ
てきて、7時半にはすっかりウェット。もうこりゃだめだなということで、A1
当日券買って、ピストンのナラシだけして帰ってきました。




明日も雨っぽい。スリックを二セット買ってレイン履き...
ロガーデータは久々にばっちり。ナラシで廻しても12000rpmなので、振動もノ イズも軽いというのもあるかも。序盤ラップがないのはP-LAPの電源入れ忘れて た。
経過時間機能もしっかり動いていて、残り時間がわかって楽だった。LEDの場所 が悪くて外側から覗きこまないと見えないのが問題。

ナラシで一本走っただけなのに、帰ってきたらグッタリ。二時間ほどばったり 倒れていた。まだ病みあがりかな。

途中でガムテープが剥れてしまい、58℃くらいまでしか上がらなかったのが不 安だったのだけどいいピストンができてました。これでカジってたらショック 過ぎる。リングとプラグを新品にして準備OK。雨のファイナルはこの前は 16:37だったけれど、もっとショートにして15:35で行くことにします。
レインは筑波の一戦で使ったののまま。二戦の予選も走ったかな。買っていく かどうかとても迷ったのだけど...。ギリギリもつとは思うんだよね。

ハイポはこのくらいで足りるかな...^^。さすがにこれ全部飲んでしまうと、そ の後が辛いので、できる限り飲まない方向で。



体調回復。これなら特走から走れそう。しかし、ここ体調悪い間、多めに食べ
てしまったため体重は61kg...  オーバー過ぎだ。


菜園状況。キャベツです。今日はモンシロチョウの猛攻が凄く。ハタハタとやっ てきては葉に止まり、腹を曲げて葉の裏に卵を産みつける。手ではたいてやっ ても果敢に逃げることもなくすぐ葉にとりつこうとする。気合いを見た。
卵は全部落としました。

ニンジン。ニンジンは虫がつくこともないので楽。

チンゲンサイはまた失敗風味。アブラナ科は本当きつい。虫が大好き過ぎ。大 根、キャベツ、チンゲンサイ、全部アブラナ科だ。

大根。よく育っている株の左右はヨトウムシに全滅くらった場所。ここには二 次隊として種からまた蒔いてます。

トマトはほとんど枯れてきた。まだちょっと収穫できます。

SH4A続き。昨日、実装したキャシュルーチンをテストしました。
テストバッファは、キャッシュラインサイズ(32B)でアライン。カラーの同じバッ
ファを作りたいために0x2000を足している。32KBの4wayなので8KBごとに同じカ
ラーになる。

uint8_t test_buf[0x2000 + 64] __attribute__((aligned (32)));

テストバッファをP2(uncached)から表示します。
void
dump_from_p2 ()
{
  uint32_t *p2 = (uint32_t *)((vaddr_t)test_buf | 0xa0000000);
  uint32_t *p;
  int i, j;
  iprintf ("Dump from P2\n");
  for (j = 0, p = p2; j < 1; j++)
    {
      for (i = 0; i < 8; i++)
	iprintf ("%x ", *p++);
      iprintf ("\n");
    }
}

テストプログラムはP1で動かし、P1はwrite-backに設定。
uint32_t
cache (int32_t argc, const char *argv[])
{
  struct {
    void (*cache_func)(vaddr_t, vsize_t);
    uint8_t pattern;
    const char *name;
  } cache_test [] = {
    { sh7785_dcache_wbinv_range, 0xaa, "write-back invalidate" },
    { sh7785_dcache_wb_range, 0xbb, "write-back" },
    { sh7785_dcache_inv_range, 0xcc, "invalidate" },
    { sh7785_dcache_wbinv_range_index, 0xdd, "write-back invalidate index" },
    { (void(*)(vaddr_t, vsize_t))sh7785_dcache_wbinv_all, 0xee,
      "write-back invalidate index all" },
  }, *p;
  vsize_t test_buf_size = 32;キャッシュラインを1ライン使ってテスト。

  if (argc < 2)
    return 0;
  p = cache_test + atoi (argv[1]);

コンテキストスイッチをしてキャッシュが不用意に書き戻されたりしないよう
に割り込みを禁止します。(今の段階ではシリアルの割り込みがあがってきた時
しかスイッチしないけれど。マイOSではprintfとiprintfの二種類を用意してい
る。printfはスレッドを使ったバッファド、iprintfはポーリングだけで実装し
たもの。)

  cpu_status_t s = intr_suspend ();

ここでテストバッファと同じカラーのバッファをキャッシュに乗せます。
  memset (test_buf + 0x2000, 0xa5, test_buf_size);

  iprintf ("%s test buffer P1=%x pattern=%x\n", p->name, test_buf, p->pattern);
  sh7785_dcache_array_dump ((vaddr_t)test_buf, test_buf_size);

ここでテストバッファに書き込みます。P1 write-backなので、キャッシュには
乗っているけれど、メモリにはまだ書き込まれていません。

  memset (test_buf, p->pattern, test_buf_size);
キャッシュの状況を確認。
  sh7785_dcache_array_dump ((vaddr_t)test_buf, test_buf_size);
メモリの状況を確認。
  dump_from_p2 ();

ここでキャッシュフラッシュ。
  p->cache_func ((vaddr_t)test_buf, test_buf_size);
  //  sh7785_dcache_wbinv_all ();

状況をまた確認。
  dump_from_p2 ();
  sh7785_dcache_array_dump ((vaddr_t)test_buf, test_buf_size);

  intr_resume (s);

  return 0;
}

キャッシュの状況はメモリ割り付けキャッシュから調べます。
void
sh7785_dcache_array_dump (vaddr_t va, vsize_t sz)
{
  cpu_run_P2 ();
  vsize_t eva = ROUND_CACHELINE_32 (va + sz);
  va = TRUNC_CACHELINE_32 (va);

  size_t j, k;

  while (va < eva)
    {
キャシュラインごとにダンプします。    
      uint32_t entry = va & (CCDD_ENTRY_MASK << CCDD_ENTRY_SHIFT);
      uint32_t d0 = SH7785_CCDD | entry;
      uint32_t a0 = SH7785_CCDA | entry;
      iprintf ("[Entry %x](%x)----------------------------------------------\n",
	       (va >> CCDD_ENTRY_SHIFT) & CCDD_ENTRY_MASK, entry);
全てのウェイについて
      for (k = 0; k < SH7785_CACHE_WAY; k++)
	{
	  uint32_t way = k << CCDD_WAY_SHIFT;
	  uint32_t d1 = d0 | way;
	  uint32_t bit = *(volatile uint32_t *)(a0 | way);
	  iprintf ("way %d tag=%x (%c|%c)\n", k,
		   bit & (CCDA_TAG_MASK << CCDA_TAG_SHIFT),
		   bit & CCDA_U ? 'D' : '-',
		   bit & CCDA_V ? 'V' : '-');
キャッシュに乗っているデータを表示
	  // Dump cache line.
	  for (j = 0; j < SH7785_CACHE_LINESZ / sizeof (int32_t); j++)
	    {
	      uint32_t line = j << CCDD_LINE_SHIFT;
	      iprintf ("%x ",  *(volatile uint32_t *)(d1 | line));
	    }
	  iprintf ("\n");
	}
      va += SH7785_CACHE_LINESZ;
    }

  CPU_SYNC_RESOURCE ();
  cpu_run_P1 ();
}

**********************************************************************
[write-back invalidateのテスト]

test4> cache 0
write-back invalidate test buffer P1=89006660 pattern=aa
[Entry 33](660)----------------------------------------------
way 0 tag=9008400 (D|V) 同じカラーに乗せておいたバッファがあります。
a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 
way 1 tag=9008400 (-|-)
0 0 0 0 0 0 0 0 
way 2 tag=9008400 (-|-)
4adfa541 3dd895d7 a4d1c46d d3d6f4fb 4369e96a 346ed9fc ad678846 da60b8d0 
way 3 tag=9008400 (-|-)
0 0 0 0 0 0 0 0 

ここでテストバッファに0xaaを書き込み。

[Entry 33](660)----------------------------------------------
way 0 tag=9008400 (D|V)
a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 
way 1 tag=9006400 (D|V) ここのキャッシュに乗りました。
aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa 
way 2 tag=9008400 (-|-)
4adfa541 3dd895d7 a4d1c46d d3d6f4fb 4369e96a 346ed9fc ad678846 da60b8d0 
way 3 tag=9008400 (-|-)
0 0 0 0 0 0 0 0 

メモリを覗きます。まだ書きこまれてません。
Dump from P2
0 0 0 0 0 0 0 0 

ここでsh7785_dcache_wbinv_range()

Dump from P2 書きこまれました。
aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa 

[Entry 33](660)----------------------------------------------
way 0 tag=9008400 (D|V) OCBP命令なので該当しないバッファはそのままです。
a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 
way 1 tag=9006400 (-|-) 該当キャッシュは無効化されています。
aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa aaaaaaaa 
way 2 tag=9008400 (-|-)
4adfa541 3dd895d7 a4d1c46d d3d6f4fb 4369e96a 346ed9fc ad678846 da60b8d0 
way 3 tag=9008400 (-|-)
0 0 0 0 0 0 0 0 
test4> 

**********************************************************************
[write-backのテスト]
test4> cache 1
write-back test buffer P1=89006660 pattern=bb
[Entry 33](660)----------------------------------------------
way 0 tag=9008400 (D|V)
a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 
way 1 tag=9008400 (-|-)
0 0 0 0 0 0 0 0 
way 2 tag=9008400 (-|-)
4adfa541 3dd895d7 a4d1c46d d3d6f4fb 4369e96a 346ed9fc ad678846 da60b8d0 
way 3 tag=9008400 (-|-)
0 0 0 0 0 0 0 0 
[Entry 33](660)----------------------------------------------
way 0 tag=9008400 (D|V)
a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 
way 1 tag=9006400 (D|V)
bbbbbbbb bbbbbbbb bbbbbbbb bbbbbbbb bbbbbbbb bbbbbbbb bbbbbbbb bbbbbbbb 
way 2 tag=9008400 (-|-)
4adfa541 3dd895d7 a4d1c46d d3d6f4fb 4369e96a 346ed9fc ad678846 da60b8d0 
way 3 tag=9008400 (-|-)
0 0 0 0 0 0 0 0 

Dump from P2
0 0 0 0 0 0 0 0 

ここでsh7785_dcache_wb_range()

Dump from P2 書きこまれました。
bbbbbbbb bbbbbbbb bbbbbbbb bbbbbbbb bbbbbbbb bbbbbbbb bbbbbbbb bbbbbbbb 
[Entry 33](660)----------------------------------------------
way 0 tag=9008400 (D|V) OCBWB命令なので該当しないバッファはそのままです。
a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 
way 1 tag=9006400 (-|V) write-backだけされてキャッシュは有効です。
bbbbbbbb bbbbbbbb bbbbbbbb bbbbbbbb bbbbbbbb bbbbbbbb bbbbbbbb bbbbbbbb 
way 2 tag=9008400 (-|-)
4adfa541 3dd895d7 a4d1c46d d3d6f4fb 4369e96a 346ed9fc ad678846 da60b8d0 
way 3 tag=9008400 (-|-)
0 0 0 0 0 0 0 0 
test4> 

**********************************************************************
[invalidateのテスト]
test4> cache 2
invalidate test buffer P1=89006660 pattern=cc
[Entry 33](660)----------------------------------------------
way 0 tag=9008400 (D|V)
a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 
way 1 tag=9008400 (-|-)
0 0 0 0 0 0 0 0 
way 2 tag=9008400 (-|-)
4adfa541 3dd895d7 a4d1c46d d3d6f4fb 4369e96a 346ed9fc ad678846 da60b8d0 
way 3 tag=9008400 (-|-)
0 0 0 0 0 0 0 0 
[Entry 33](660)----------------------------------------------
way 0 tag=9008400 (D|V)
a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 
way 1 tag=9006400 (D|V)
cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc 
way 2 tag=9008400 (-|-)
4adfa541 3dd895d7 a4d1c46d d3d6f4fb 4369e96a 346ed9fc ad678846 da60b8d0 
way 3 tag=9008400 (-|-)
0 0 0 0 0 0 0 0 

Dump from P2
0 0 0 0 0 0 0 0 

ここでsh7785_dcache_inv_range()

Dump from P2 キャッシュを無効化しただけなので書きこまれません。
0 0 0 0 0 0 0 0 
[Entry 33](660)----------------------------------------------
way 0 tag=9008400 (D|V)
a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 
way 1 tag=9006400 (-|-) キャッシュは無効化されています。
cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc cccccccc 
way 2 tag=9008400 (-|-)
4adfa541 3dd895d7 a4d1c46d d3d6f4fb 4369e96a 346ed9fc ad678846 da60b8d0 
way 3 tag=9008400 (-|-)
0 0 0 0 0 0 0 0 
test4> 

**********************************************************************
[write-back invalidate (Index ops)のテスト]
test4> cache 3
write-back invalidate index test buffer P1=89006660 pattern=dd
[Entry 33](660)----------------------------------------------
way 0 tag=9008400 (D|V)
a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 
way 1 tag=9008400 (-|-)
0 0 0 0 0 0 0 0 
way 2 tag=9008400 (-|-)
4adfa541 3dd895d7 a4d1c46d d3d6f4fb 4369e96a 346ed9fc ad678846 da60b8d0 
way 3 tag=9008400 (-|-)
0 0 0 0 0 0 0 0 
[Entry 33](660)----------------------------------------------
way 0 tag=9008400 (D|V)
a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 
way 1 tag=9006400 (D|V)
dddddddd dddddddd dddddddd dddddddd dddddddd dddddddd dddddddd dddddddd 
way 2 tag=9008400 (-|-)
4adfa541 3dd895d7 a4d1c46d d3d6f4fb 4369e96a 346ed9fc ad678846 da60b8d0 
way 3 tag=9008400 (-|-)
0 0 0 0 0 0 0 0 

Dump from P2
0 0 0 0 0 0 0 0 

ここでsh7785_dcache_wbinv_range_index()

Dump from P2 書きこまれました。
dddddddd dddddddd dddddddd dddddddd dddddddd dddddddd dddddddd dddddddd 
[Entry 33](660)----------------------------------------------
way 0 tag=9008400 (-|-) インデックス操作なのでここも書き戻されて無効化されます。
a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 a5a5a5a5 
way 1 tag=9006400 (-|-) 書き戻されて無効化されました。
dddddddd dddddddd dddddddd dddddddd dddddddd dddddddd dddddddd dddddddd 
way 2 tag=9008400 (-|-)
4adfa541 3dd895d7 a4d1c46d d3d6f4fb 4369e96a 346ed9fc ad678846 da60b8d0 
way 3 tag=9008400 (-|-)
0 0 0 0 0 0 0 0 
test4>          

MMUまわりについて。
SH4とSH4Aの違いについて。
SH4までは29bitの物理空間だったのが、SH4Aからは32ビットアドレス拡張モー ドが導入され、32bitの物理空間を扱えるようになった。 その場合、P1,P2の扱いが変更になり、PMB(特権マッピングバッファ)にP1,P2の マッピングを割と自由に設定できるようになった。16エントリあり、ページは 16M,64M,128M,512Mが設定可能。
SH4A 7780と7785の違いについて。
7780ではページサイズ、ページの保護キーはSH4と同じだった。7785はこれを互 換モードとし、さらにTLB拡張モードが追加。ページサイズの幅が拡がった他、 ページのプロテクションが特権のRWX,ユーザのRWXの計6bitによる自由な組み合 わせが可能になった。今迄ページサイズはPTELのSZ,PRで設定してLDTLBしてい たが、PTELのSZ,PRは使われなくなり、PTEAに指定したESZ, EPRがLDTLBの際に ロードされることになる。メモリマップドからは新しくできたITLBデータアレ イ2,UTLBデータアレイ2から設定ができる。これに伴い、メモリマップドから設 定する時はITLBデータアレイ1に書きこんだら、その後必ず対応するITLBデータ アレイ2のエントリも設定しないといけない。
PTEAがいろいろ違う。SH4:7750,7750S,7750RではPTEAはPCMCIA 空間のアクセス のための設定だった。SH4AではPCMCIA空間の設定がなくなったので7780はPTEA がない。7785ではTLB拡張モードの導入によって保護とページサイズの設定のた めのPTEAが復活したけれど、その使い方はSH4とはまったく違う。