tilemap renderer

video_tilemap.nsl は結構長いのではしょってはっつける。

module tilemap_renderer{
	reg offset_x[9] = 9'd0;
	reg llayer = 0; //0:tilemap#0 1:tilemap#2
	wire scroll_y[3];
	reg vram_aa[22]; reg vram_ce;
	reg lb_latchh;
	reg flip_x; reg priority[2]; reg color[4];
	
	tilemap_scroll_addressing map0, map1;
	proc_name render();

宣言部。output reg が使えないので出力端子名と内部配線名がかぶって雑になってしまっている。命名規則を決めないといけない。

	{
		map0.layer = 0;
		map0.offset_x = offset_x;
		map0.scroll_x = map0_scroll_x;
		map0.offset_y = dot_y[7:0]; //?
		map0.scroll_y = map0_scroll_y;
		
		map1.layer = 1;
		map1.offset_x = offset_x;
		map1.scroll_x = map1_scroll_x;
		map1.offset_y = dot_y[7:0];
		map1.scroll_y = map1_scroll_y;
	}

tilemap RAM のアドレス算出は スクロールの値を加算してから bitfield を切り出したりしてちょっと面倒。これを2回やらないといけないので Verilog でいう function の役目が欲しかった。

NSL の func の使い方をいまだによく理解してないのでモジュールを作ったんが、それ以前にこれを2つ作る必要がないな。後日直す。

	if(render_start == 1'b0){
		render();
	}

以前手こずった state machine の呼び出し。 render_start が 0 を検出し次第state machine が動き出す。

	proc render{
		state_name init, tilemap_wait, char_wait, offset_update, tilemap_set;
		
		state init{
			(略)
			goto tilemap_wait;
		}
		state tilemap_wait{
			(略)
		}
		state char_wait{
			(略)
		}
		state offset_update{
			lb_latchh := 1'b0;
			if(offset_x < 384 + 8){
				offset_x := offset_x + 8;
				goto tilemap_set;
			}else if(llayer == 0){
				offset_x := 0;
				llayer := 1;
				goto tilemap_set;
			}else{
				llayer := 0;
				offset_x := 0;
				finish();
				goto init;
			}
		}
		state tilemap_set{
			vram_aa := if(llayer == 0) map0.a else map1.a;
			vram_ce := 0;
			goto tilemap_wait;
		}
	}

offset_update のところに finish() と goto init; がある。各レジスタの代入とともに state が init に設定されたところで render はいったん止まる。止まった時点で render_start が 0 かの検出が再開される。

goto init を抜くと再開される state は offset_update からになるので意味がないので注意。他に気になるのは state が変数として取れないので state が char_wait の時は assignment を切り換えるということができない。

このいった処理で一通り state machine が意図通りに動いていることをシミュレータで確認できた。output reg が使えないとか、bitfield の代入をばらすことができないとか、一部では不満はあるが、タイプ数の軽減は圧倒的なのでもうちょっと使いこなせるようにしていけば、コーディングの時間が減らせることだろう。