Home記事一覧フォーラム

Synthesijer用スプライト/キャラクター表示ライブラリの作成

スプライト/キャラクター表示ライブラリを作りました。Synthesijerからインスタンス化して簡単に使用することができます。

特長:

ターゲットボードについて

このプロジェクトはFPGA開発ボード「Terasic DE0-CV」用です。

プロジェクトのダウンロード、ビルド

Linux上でのビルドを想定しています。
Synthesijerはあらかじめ「高位合成ツール「Synthesijer」を使う」の方法で、また、Quartus Primeは「AlteraのFPGA開発ツール「Quartus Prime」をUbuntuにインストールする」の方法でインストールしているものとします。

ダウンロード:sprite_example.tar.gz

tar xzf sprite_example.tar.gz

sprite_example/synthesijer ディレクトリに移動してmakeします。

cd sprite_example/synthesijer

make

Quartus Ver.15.0以上 でプロジェクトファイル sprite_example/de0-cv/de0_cv_start.qpf を開いて「Start Compilation」、「Programmer」で転送して実行します。

使い方

スプライトやキャラクターを使う際には、VgaIfaceを同時にインスタンス化します。
(例)private final VgaIface vga = new VgaIface();
画面サイズは640x480ピクセル。
色は8bitで、{R 3bit, G 3bit, B 2bit} で指定します。
スプライト、キャラクターともに色0 (r,g,b=0)は透明色になります。不透明な黒が必要な場合は最も暗い赤などで代用してください。

スプライト:
生成:private final Sprite spr = new Sprite("SPRITE_SIZE_BITS", "6");
(SPRITE_SIZE_BITSの指定はオプション。指定すれば任意のサイズの回路を生成できます。サイズをビット数で表します。6bit:64x64)
スプライトのビットマップデータへのアクセス(write only):spr.bitmap[i] = (byte)a;
表示座標:spr.x = a; spr.y = b; 原点は左上です。
拡大縮小:spr.scale = a; (0 <= a <= 15。表示倍率は256÷2のa乗)

キャラクター:
キャラクター1個のサイズは8x8ピクセルです。
生成:private final ChrBG bg = new ChrBG("CHR_SIZE_BITS", "6", "BITMAP_BITS", "2");
(CHR_SIZE_BITS, BITMAP_BITSの指定はオプション。CHR_SIZE_BITSで画面にキャラクターを並べる個数を指定できます。BITMAP_BITSはビットマップデータのBPP(1 or 2)です。)
キャラクターのビットマップデータへのアクセス(write only):bg.bitmap[i] = (byte)a; (下位2bitのみ有効)
キャラクター配置データへのアクセス(write only):bg.chr[i] = (byte)a;
1キャラクターあたり8bitの仮想画面の配列です。上位2bitがパレットNo.で、下位6bitがキャラクターNo.です。
表示座標:bg.x = a; bg.y = b;
拡大縮小:bg.scale = a; キャラクター画面がVGA画面よりも小さい場合、画面端はループして表示されます。
パレット色指定:bg.palette0 = c0; bg.palette1 = c1; bg.palette2 = c2; bg.palette3 = c3;
色(8bit)x4の32bitの値を指定します。{色3,色2,色1,色0}

キャラクターパターンのROMは sprite_example/synthesijer/MakeROM/MakeChrROM.java で生成します。
横8ピクセルx縦8ピクセルx64キャラクターの順で色番号(0-3)を指定します。
このサンプルではASCIIキャラクターパターンが入っています。キャラクターコードは(ASCIIコード - 32)から64文字分です。

トップモジュールの作成:
作り方は sprite_example/de0-cv/de0_cv_start.v を参考にしてください。
ソースファイルを追加した場合は sprite_example/de0-cv/de0_cv_start.qsf の「# FILES」の部分の記述に追加します。
Synthesijerで一度コンパイルし、Synthesijer側トップモジュール(ここでは SpriteExample.java をコンパイルしてできた SpriteExample.v) のポート・リストを確認して de0_cv_start.v に配線を記述します。
スプライト、キャラクターそれぞれにビデオクロック、それに対応するリセット、VGAインターフェイスから出力されるH, Vカウント(count_h, count_v)を接続し、表示プライオリティの低い順にcolor[x]を接続します。
スプライト、キャラクターの総数を「localparam PLANES = x;」に記述します。
「// connector」と書かれている部分は重ね合わせ表示の回路です。このままコピーしてください。

ソースコード

これらのソースコードはBSD 2-Clauseライセンスで公開します。

vga_iface.v : VGA出力モジュール本体
/*
  Copyright (c) 2015-2016, miya
  All rights reserved.

  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/


module vga_iface
  (
   input                    clk,
   input                    reset,

   output                   vsync,
   output signed [32-1 : 0] vcount,
   input                    ext_clkv,
   input                    ext_resetv,
   input signed [8-1 : 0]   ext_color,
   output                   ext_vga_hs,
   output                   ext_vga_vs,
   output                   ext_vga_de,
   output signed [8-1 : 0]  ext_vga_r,
   output signed [8-1 : 0]  ext_vga_g,
   output signed [8-1 : 0]  ext_vga_b,
   output signed [32-1 : 0] ext_count_h,
   output signed [32-1 : 0] ext_count_v
   );

  localparam VGA_MAX_H = 799; // 800-1
  localparam VGA_MAX_V = 524; // 525-1
  localparam VGA_WIDTH = 640;
  localparam VGA_HEIGHT = 480;
  localparam VGA_SYNC_H_START = 656;
  localparam VGA_SYNC_V_START = 490;
  localparam VGA_SYNC_H_END = 752;
  localparam VGA_SYNC_V_END = 492;
  localparam DOT_BITS = 8;
  localparam LINE_BITS = 10;
  localparam PIXEL_DELAY = 8;

  reg [LINE_BITS-1:0]       count_h;
  reg [LINE_BITS-1:0]       count_v;
  wire                      vga_hs;
  wire                      vga_vs;
  wire                      pixel_valid;
  wire                      vga_hs_delay;
  wire                      vga_vs_delay;
  wire                      pixel_valid_delay;

  // H counter
  always @(posedge ext_clkv)
    begin
      if (ext_resetv == 1'b1)
        begin
          count_h <= 1'd0;
        end
      else
        begin
          if (count_h == VGA_MAX_H)
            begin
              count_h <= 1'd0;
            end
          else
            begin
              count_h <= count_h + 1'd1;
            end
        end
    end

  // V counter
  always @(posedge ext_clkv)
    begin
      if (ext_resetv == 1'b1)
        begin
          count_v <= 1'd0;
        end
      else
        begin
          if (count_h == VGA_MAX_H)
            begin
              if (count_v == VGA_MAX_V)
                begin
                  count_v <= 1'd0;
                end
              else
                begin
                  count_v <= count_v + 1'd1;
                end
            end
        end
    end

  // H sync
  assign vga_hs = ((count_h >= VGA_SYNC_H_START) && (count_h < VGA_SYNC_H_END)) ? 1'b0 : 1'b1;
  // V sync
  assign vga_vs = ((count_v >= VGA_SYNC_V_START) && (count_v < VGA_SYNC_V_END)) ? 1'b0 : 1'b1;
  // Pixel valid
  assign pixel_valid = ((count_h < VGA_WIDTH) && (count_v < VGA_HEIGHT)) ? 1'b1 : 1'b0;

  // ext out
  assign ext_vga_r = pixel_valid_delay ? {ext_color[7:5], 1'b0} : 4'd0;
  assign ext_vga_g = pixel_valid_delay ? {ext_color[4:2], 1'b0} : 4'd0;
  assign ext_vga_b = pixel_valid_delay ? {ext_color[1:0], 2'b0} : 4'd0;
  assign ext_vga_hs = vga_hs_delay;
  assign ext_vga_vs = vga_vs_delay;
  assign ext_vga_de = pixel_valid_delay;
  assign ext_count_h = count_h;
  assign ext_count_v = count_v;


  cdc_synchronizer
    #(
      .DATA_WIDTH (1)
      )
  sync_vsync
    (
     .clk_in (ext_clkv),
     .clk_out (clk),
     .data_in (ext_vga_vs),
     .data_out (vsync),
     .reset_in (ext_resetv)
     );

  cdc_synchronizer
    #(
      .DATA_WIDTH (32)
      )
  sync_vcount
    (
     .clk_in (ext_clkv),
     .clk_out (clk),
     .data_in (count_v),
     .data_out (vcount),
     .reset_in (ext_resetv)
     );

  shift_register_vector
    #(
      .WIDTH (1),
      .DEPTH (PIXEL_DELAY)
      )
  shift_register_vector_vga_hs
    (
     .clk (ext_clkv),
     .data_in (vga_hs),
     .data_out (vga_hs_delay)
     );

  shift_register_vector
    #(
      .WIDTH (1),
      .DEPTH (PIXEL_DELAY)
      )
  shift_register_vector_vga_vs
    (
     .clk (ext_clkv),
     .data_in (vga_vs),
     .data_out (vga_vs_delay)
     );

  shift_register_vector
    #(
      .WIDTH (1),
      .DEPTH (PIXEL_DELAY)
      )
  shift_register_vector_pixel_valid
    (
     .clk (ext_clkv),
     .data_in (pixel_valid),
     .data_out (pixel_valid_delay)
     );

endmodule

VgaIface.java : VGA出力モジュールのSynthesijer用ラッパークラス
/*
  Copyright (c) 2015-2016, miya
  All rights reserved.

  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/


import java.util.EnumSet;

import synthesijer.hdl.HDLModule;
import synthesijer.hdl.HDLPort;
import synthesijer.hdl.HDLPort.DIR;
import synthesijer.hdl.HDLPrimitiveType;

public class VgaIface extends HDLModule
{
  public boolean vsync;
  public int vcount;

  public VgaIface(String... args)
  {
    super("vga_iface", "clk", "reset");

    newPort("vsync", DIR.OUT, HDLPrimitiveType.genBitType());
    newPort("vcount", DIR.OUT, HDLPrimitiveType.genSignedType(32));

    newPort("ext_clkv", DIR.IN, HDLPrimitiveType.genBitType(), EnumSet.of(HDLPort.OPTION.EXPORT));
    newPort("ext_resetv", DIR.IN, HDLPrimitiveType.genBitType(), EnumSet.of(HDLPort.OPTION.EXPORT));
    newPort("ext_color", DIR.IN, HDLPrimitiveType.genSignedType(8), EnumSet.of(HDLPort.OPTION.EXPORT));
    newPort("ext_vga_hs", DIR.OUT, HDLPrimitiveType.genBitType(), EnumSet.of(HDLPort.OPTION.EXPORT));
    newPort("ext_vga_vs", DIR.OUT, HDLPrimitiveType.genBitType(), EnumSet.of(HDLPort.OPTION.EXPORT));
    newPort("ext_vga_de", DIR.OUT, HDLPrimitiveType.genBitType(), EnumSet.of(HDLPort.OPTION.EXPORT));
    newPort("ext_vga_r", DIR.OUT, HDLPrimitiveType.genSignedType(8), EnumSet.of(HDLPort.OPTION.EXPORT));
    newPort("ext_vga_g", DIR.OUT, HDLPrimitiveType.genSignedType(8), EnumSet.of(HDLPort.OPTION.EXPORT));
    newPort("ext_vga_b", DIR.OUT, HDLPrimitiveType.genSignedType(8), EnumSet.of(HDLPort.OPTION.EXPORT));
    newPort("ext_count_h", DIR.OUT, HDLPrimitiveType.genSignedType(32), EnumSet.of(HDLPort.OPTION.EXPORT));
    newPort("ext_count_v", DIR.OUT, HDLPrimitiveType.genSignedType(32), EnumSet.of(HDLPort.OPTION.EXPORT));
  }
}

sprite.v : スプライト・モジュール本体
/*
  Copyright (c) 2016, miya
  All rights reserved.

  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/


module sprite
  #(
    parameter SPRITE_SIZE_BITS = 6
    )
  (
   input                     clk,
   input                     reset,

   output signed [32-1:0]    bitmap_length,
   input signed [32-1:0]     bitmap_address,
   input signed [8-1:0]      bitmap_din,
   output signed [8-1:0]     bitmap_dout,
   input                     bitmap_we,
   input                     bitmap_oe,

   input signed [32-1:0]     x,
   input signed [32-1:0]     y,
   input signed [32-1:0]     scale,

   input                     ext_clkv,
   input                     ext_resetv,
   output reg signed [8-1:0] ext_color,
   input signed [32-1:0]     ext_count_h,
   input signed [32-1:0]     ext_count_v
   );

  localparam VGA_WIDTH = 640;
  localparam VGA_HEIGHT = 480;
  localparam BPP = 8;
  localparam OFFSET_BITS = 16;
  localparam ADDR_BITS = (SPRITE_SIZE_BITS * 2);
  localparam SCALE_BITS_BITS = 4;
  localparam SCALE_BITS = (1 << SCALE_BITS_BITS);
  localparam SCALE_DIV_BITS = 8;
  localparam SPRITE_SIZE = (1 << SPRITE_SIZE_BITS);

  // return const value
  assign bitmap_length = 1 << ADDR_BITS;
  assign bitmap_dout = 1'd0;

  // inside the sprite
  wire                       vp_sprite_inside_d2;
  wire                       vp_sprite_inside_d4;
  assign vp_sprite_inside_d2 = ((dx1_d2 >= 0) &&
                                (dx1_d2 < SPRITE_SIZE) &&
                                (dy1_d2 >= 0) &&
                                (dy1_d2 < SPRITE_SIZE));

  // sprite logic
  reg [BPP-1:0]              color;
  reg signed [OFFSET_BITS+SCALE_BITS-1:0] dx0_d1;
  reg signed [OFFSET_BITS+SCALE_BITS-1:0] dy0_d1;
  reg signed [OFFSET_BITS+SCALE_BITS-1:0] dx1_d2;
  reg signed [OFFSET_BITS+SCALE_BITS-1:0] dy1_d2;
  reg [ADDR_BITS-1:0]                     bitmap_raddr_d3;
  wire [BPP-1:0]                          bitmap_color_d4;
  wire signed [OFFSET_BITS-1:0]           x_sync;
  wire signed [OFFSET_BITS-1:0]           y_sync;
  wire [SCALE_BITS_BITS-1:0]              scale_sync;
  reg [SCALE_BITS_BITS-1:0]               scale_sync_d1;
  reg signed [BPP-1:0]                    color_d5;
  reg signed [BPP-1:0]                    color_d6;

  always @(posedge ext_clkv)
    begin
      dx0_d1 <= ext_count_h - x_sync;
      dy0_d1 <= ext_count_v - y_sync;
      scale_sync_d1 <= scale_sync;
      dx1_d2 <= (dx0_d1 << scale_sync_d1) >> SCALE_DIV_BITS;
      dy1_d2 <= (dy0_d1 << scale_sync_d1) >> SCALE_DIV_BITS;
      bitmap_raddr_d3 <= (dy1_d2[SPRITE_SIZE_BITS-1:0] << SPRITE_SIZE_BITS) + dx1_d2[SPRITE_SIZE_BITS-1:0];
      color_d5 <= vp_sprite_inside_d4 ? bitmap_color_d4 : 1'd0;
      color_d6 <= color_d5;
      ext_color <= color_d6; // ext_color: delay 7
    end

  // bitmap
  dual_port_ram
    #(
      .DATA_WIDTH (BPP),
      .ADDR_WIDTH (ADDR_BITS)
      )
  dual_port_ram_0
    (
     .data_in (bitmap_din),
     .read_addr (bitmap_raddr_d3),
     .write_addr (bitmap_address),
     .we (bitmap_we),
     .read_clock (ext_clkv),
     .write_clock (clk),
     .data_out (bitmap_color_d4)
     );

  cdc_synchronizer
    #(
      .DATA_WIDTH (OFFSET_BITS)
      )
  cdc_synchronizer_x
    (
     .clk_in (clk),
     .clk_out (ext_clkv),
     .data_in (x[OFFSET_BITS-1:0]),
     .data_out (x_sync),
     .reset_in (reset)
     );

  cdc_synchronizer
    #(
      .DATA_WIDTH (OFFSET_BITS)
      )
  cdc_synchronizer_y
    (
     .clk_in (clk),
     .clk_out (ext_clkv),
     .data_in (y[OFFSET_BITS-1:0]),
     .data_out (y_sync),
     .reset_in (reset)
     );

  cdc_synchronizer
    #(
      .DATA_WIDTH (SCALE_BITS)
      )
  cdc_synchronizer_scale
    (
     .clk_in (clk),
     .clk_out (ext_clkv),
     .data_in (scale[SCALE_BITS-1:0]),
     .data_out (scale_sync),
     .reset_in (reset)
     );

  shift_register_vector
    #(
      .WIDTH (1),
      .DEPTH (2)
      )
  shift_register_vector_vp_sprite_inside_d4
    (
     .clk (ext_clkv),
     .data_in (vp_sprite_inside_d2),
     .data_out (vp_sprite_inside_d4)
     );

endmodule

Sprite.java : スプライト・ラッパークラス
/*
  Copyright (c) 2016, miya
  All rights reserved.

  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/


import java.util.EnumSet;

import synthesijer.hdl.HDLModule;
import synthesijer.hdl.HDLPort;
import synthesijer.hdl.HDLPort.DIR;
import synthesijer.hdl.HDLPrimitiveType;

public class Sprite extends HDLModule
{
  public byte[] bitmap;
  public int x;
  public int y;
  public int scale;

  public Sprite(String... args)
  {
    super("sprite", "clk", "reset");

    newParameter("SPRITE_SIZE_BITS", 6);

    newPort("bitmap_length", DIR.OUT, HDLPrimitiveType.genSignedType(32));
    newPort("bitmap_address",DIR.IN, HDLPrimitiveType.genSignedType(32));
    newPort("bitmap_din",    DIR.IN, HDLPrimitiveType.genSignedType(8));
    newPort("bitmap_dout",   DIR.OUT, HDLPrimitiveType.genSignedType(8));
    newPort("bitmap_we",     DIR.IN, HDLPrimitiveType.genBitType());
    newPort("bitmap_oe",     DIR.IN, HDLPrimitiveType.genBitType());

    newPort("x", DIR.IN, HDLPrimitiveType.genSignedType(32));
    newPort("y", DIR.IN, HDLPrimitiveType.genSignedType(32));
    newPort("scale", DIR.IN, HDLPrimitiveType.genSignedType(32));

    newPort("ext_clkv", DIR.IN, HDLPrimitiveType.genBitType(), EnumSet.of(HDLPort.OPTION.EXPORT));
    newPort("ext_resetv", DIR.IN, HDLPrimitiveType.genBitType(), EnumSet.of(HDLPort.OPTION.EXPORT));

    newPort("ext_color", DIR.OUT, HDLPrimitiveType.genSignedType(32), EnumSet.of(HDLPort.OPTION.EXPORT));
    newPort("ext_count_h", DIR.IN, HDLPrimitiveType.genSignedType(32), EnumSet.of(HDLPort.OPTION.EXPORT));
    newPort("ext_count_v", DIR.IN, HDLPrimitiveType.genSignedType(32), EnumSet.of(HDLPort.OPTION.EXPORT));
  }
}

chr_bg.v : キャラクター・モジュール本体
/*
  Copyright (c) 2016, miya
  All rights reserved.

  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/


module chr_bg
  #(
    parameter CHR_SIZE_BITS = 6,
    parameter BITMAP_BITS = 2
    )
  (
   input                     clk,
   input                     reset,

   output signed [32-1:0]    chr_length,
   input signed [32-1:0]     chr_address,
   input signed [8-1:0]      chr_din,
   output signed [8-1:0]     chr_dout,
   input                     chr_we,
   input                     chr_oe,

   output signed [32-1:0]    bitmap_length,
   input signed [32-1:0]     bitmap_address,
   input signed [8-1:0]      bitmap_din,
   output signed [8-1:0]     bitmap_dout,
   input                     bitmap_we,
   input                     bitmap_oe,

   input signed [32-1:0]     x,
   input signed [32-1:0]     y,
   input signed [32-1:0]     scale,
   input signed [32-1:0]     palette0,
   input signed [32-1:0]     palette1,
   input signed [32-1:0]     palette2,
   input signed [32-1:0]     palette3,

   input                     ext_clkv,
   input                     ext_resetv,
   output reg signed [8-1:0] ext_color,
   input signed [32-1:0]     ext_count_h,
   input signed [32-1:0]     ext_count_v
   );

  localparam VGA_WIDTH = 640;
  localparam VGA_HEIGHT = 480;
  localparam INT_BITS = 32;
  localparam BPP = 8;
  localparam CHR_BITS = 8;
  localparam OFFSET_BITS = 16;
  localparam ADDR_BITS = (CHR_SIZE_BITS * 2);
  localparam SCALE_BITS_BITS = 4;
  localparam SCALE_BITS = (1 << SCALE_BITS_BITS);
  localparam SCALE_DIV_BITS = 8;
  localparam CHR_SIZE = (1 << CHR_SIZE_BITS);
  localparam BITMAP_ADDR_BITS = 12;
  localparam BITMAP_DATA_BITS = 2;
  localparam BITMAP_SIZE_BITS = 3;

  // return const value
  assign chr_length = 1 << ADDR_BITS;
  assign chr_dout = 1'd0;
  assign bitmap_length = 1 << ADDR_BITS;
  assign bitmap_dout = 1'd0;

  // logic
  reg signed [OFFSET_BITS+SCALE_BITS-1:0] dx0_d1;
  reg signed [OFFSET_BITS+SCALE_BITS-1:0] dy0_d1;
  reg signed [OFFSET_BITS+SCALE_BITS-1:0] dx1_d2;
  reg signed [OFFSET_BITS+SCALE_BITS-1:0] dy1_d2;
  reg [ADDR_BITS-1:0]                     chr_raddr_d3;
  wire [CHR_BITS-1:0]                     chr_name_d4;
  wire [CHR_BITS-1:0]                     chr_name_d6;
  wire signed [OFFSET_BITS-1:0]           x_sync;
  wire signed [OFFSET_BITS-1:0]           y_sync;
  wire [SCALE_BITS_BITS-1:0]              scale_sync;
  wire [INT_BITS-1:0]                     palette0_sync;
  wire [INT_BITS-1:0]                     palette1_sync;
  wire [INT_BITS-1:0]                     palette2_sync;
  wire [INT_BITS-1:0]                     palette3_sync;
  reg [SCALE_BITS_BITS-1:0]               scale_sync_d1;
  reg [BITMAP_ADDR_BITS-1:0]              bitmap_addr0_d3;
  reg [BITMAP_ADDR_BITS-1:0]              bitmap_addr0_d4;
  reg [BITMAP_ADDR_BITS-1:0]              bitmap_addr1_d5;
  wire [BITMAP_DATA_BITS-1:0]             bitmap_data_d6;

  always @(posedge ext_clkv)
    begin
      dx0_d1 <= ext_count_h - x_sync;
      dy0_d1 <= ext_count_v - y_sync;
      scale_sync_d1 <= scale_sync;
      dx1_d2 <= (dx0_d1 << scale_sync_d1) >> SCALE_DIV_BITS;
      dy1_d2 <= (dy0_d1 << scale_sync_d1) >> SCALE_DIV_BITS;
      chr_raddr_d3 <= (dy1_d2[CHR_SIZE_BITS+BITMAP_SIZE_BITS-1:BITMAP_SIZE_BITS] << CHR_SIZE_BITS) + dx1_d2[CHR_SIZE_BITS+BITMAP_SIZE_BITS-1:BITMAP_SIZE_BITS];
      bitmap_addr0_d3 <= (dy1_d2[BITMAP_SIZE_BITS-1:0] << BITMAP_SIZE_BITS) + dx1_d2[BITMAP_SIZE_BITS-1:0];
      bitmap_addr0_d4 <= bitmap_addr0_d3;
      bitmap_addr1_d5 <= (chr_name_d4 << (BITMAP_SIZE_BITS << 1)) + bitmap_addr0_d4;
      case ({chr_name_d6[7:6], bitmap_data_d6})
        // ext_color: 7 cycle delay
        4'b0000: ext_color <= palette0_sync[7:0];
        4'b0001: ext_color <= palette0_sync[15:8];
        4'b0010: ext_color <= palette0_sync[23:16];
        4'b0011: ext_color <= palette0_sync[31:24];

        4'b0100: ext_color <= palette1_sync[7:0];
        4'b0101: ext_color <= palette1_sync[15:8];
        4'b0110: ext_color <= palette1_sync[23:16];
        4'b0111: ext_color <= palette1_sync[31:24];

        4'b1000: ext_color <= palette2_sync[7:0];
        4'b1001: ext_color <= palette2_sync[15:8];
        4'b1010: ext_color <= palette2_sync[23:16];
        4'b1011: ext_color <= palette2_sync[31:24];

        4'b1100: ext_color <= palette3_sync[7:0];
        4'b1101: ext_color <= palette3_sync[15:8];
        4'b1110: ext_color <= palette3_sync[23:16];
        4'b1111: ext_color <= palette3_sync[31:24];
      endcase
    end

  // Display Data RAM
  dual_port_ram
    #(
      .DATA_WIDTH (CHR_BITS),
      .ADDR_WIDTH (ADDR_BITS)
      )
  dual_port_ram_0
    (
     .data_in (chr_din),
     .read_addr (chr_raddr_d3),
     .write_addr (chr_address),
     .we (chr_we),
     .read_clock (ext_clkv),
     .write_clock (clk),
     .data_out (chr_name_d4)
     );

  // Character Generator RAM
  dual_port_ram
    #(
      .DATA_WIDTH (BITMAP_BITS),
      .ADDR_WIDTH (ADDR_BITS)
      )
  dual_port_ram_1
    (
     .data_in (bitmap_din),
     .read_addr (bitmap_addr1_d5),
     .write_addr (bitmap_address),
     .we (bitmap_we),
     .read_clock (ext_clkv),
     .write_clock (clk),
     .data_out (bitmap_data_d6)
     );

  cdc_synchronizer
    #(
      .DATA_WIDTH (OFFSET_BITS)
      )
  cdc_synchronizer_x
    (
     .clk_in (clk),
     .clk_out (ext_clkv),
     .data_in (x[OFFSET_BITS-1:0]),
     .data_out (x_sync),
     .reset_in (reset)
     );

  cdc_synchronizer
    #(
      .DATA_WIDTH (OFFSET_BITS)
      )
  cdc_synchronizer_y
    (
     .clk_in (clk),
     .clk_out (ext_clkv),
     .data_in (y[OFFSET_BITS-1:0]),
     .data_out (y_sync),
     .reset_in (reset)
     );

  cdc_synchronizer
    #(
      .DATA_WIDTH (SCALE_BITS)
      )
  cdc_synchronizer_scale
    (
     .clk_in (clk),
     .clk_out (ext_clkv),
     .data_in (scale[SCALE_BITS-1:0]),
     .data_out (scale_sync),
     .reset_in (reset)
     );

  cdc_synchronizer
    #(
      .DATA_WIDTH (INT_BITS)
      )
  cdc_synchronizer_palette0
    (
     .clk_in (clk),
     .clk_out (ext_clkv),
     .data_in (palette0),
     .data_out (palette0_sync),
     .reset_in (reset)
     );

  cdc_synchronizer
    #(
      .DATA_WIDTH (INT_BITS)
      )
  cdc_synchronizer_palette1
    (
     .clk_in (clk),
     .clk_out (ext_clkv),
     .data_in (palette1),
     .data_out (palette1_sync),
     .reset_in (reset)
     );

  cdc_synchronizer
    #(
      .DATA_WIDTH (INT_BITS)
      )
  cdc_synchronizer_palette2
    (
     .clk_in (clk),
     .clk_out (ext_clkv),
     .data_in (palette2),
     .data_out (palette2_sync),
     .reset_in (reset)
     );

  cdc_synchronizer
    #(
      .DATA_WIDTH (INT_BITS)
      )
  cdc_synchronizer_palette3
    (
     .clk_in (clk),
     .clk_out (ext_clkv),
     .data_in (palette3),
     .data_out (palette3_sync),
     .reset_in (reset)
     );

  shift_register_vector
    #(
      .WIDTH (CHR_BITS),
      .DEPTH (2)
      )
  shift_register_vector_chr_name_d6
    (
     .clk (ext_clkv),
     .data_in (chr_name_d4),
     .data_out (chr_name_d6)
     );

endmodule

ChrBG.java : キャラクター・ラッパークラス
/*
  Copyright (c) 2016, miya
  All rights reserved.

  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/


import java.util.EnumSet;

import synthesijer.hdl.HDLModule;
import synthesijer.hdl.HDLPort;
import synthesijer.hdl.HDLPort.DIR;
import synthesijer.hdl.HDLPrimitiveType;

public class ChrBG extends HDLModule
{
  public byte[] chr;
  public byte[] bitmap;
  public int x;
  public int y;
  public int scale;
  public int palette0;
  public int palette1;
  public int palette2;
  public int palette3;

  public ChrBG(String... args)
  {
    super("chr_bg", "clk", "reset");

    newParameter("CHR_SIZE_BITS", 6);
    newParameter("BITMAP_BITS", 2);

    newPort("chr_length", DIR.OUT, HDLPrimitiveType.genSignedType(32));
    newPort("chr_address",DIR.IN, HDLPrimitiveType.genSignedType(32));
    newPort("chr_din",    DIR.IN, HDLPrimitiveType.genSignedType(8));
    newPort("chr_dout",   DIR.OUT, HDLPrimitiveType.genSignedType(8));
    newPort("chr_we",     DIR.IN, HDLPrimitiveType.genBitType());
    newPort("chr_oe",     DIR.IN, HDLPrimitiveType.genBitType());

    newPort("bitmap_length", DIR.OUT, HDLPrimitiveType.genSignedType(32));
    newPort("bitmap_address",DIR.IN, HDLPrimitiveType.genSignedType(32));
    newPort("bitmap_din",    DIR.IN, HDLPrimitiveType.genSignedType(8));
    newPort("bitmap_dout",   DIR.OUT, HDLPrimitiveType.genSignedType(8));
    newPort("bitmap_we",     DIR.IN, HDLPrimitiveType.genBitType());
    newPort("bitmap_oe",     DIR.IN, HDLPrimitiveType.genBitType());

    newPort("x", DIR.IN, HDLPrimitiveType.genSignedType(32));
    newPort("y", DIR.IN, HDLPrimitiveType.genSignedType(32));
    newPort("scale", DIR.IN, HDLPrimitiveType.genSignedType(32));
    newPort("palette0", DIR.IN, HDLPrimitiveType.genSignedType(32));
    newPort("palette1", DIR.IN, HDLPrimitiveType.genSignedType(32));
    newPort("palette2", DIR.IN, HDLPrimitiveType.genSignedType(32));
    newPort("palette3", DIR.IN, HDLPrimitiveType.genSignedType(32));

    newPort("ext_clkv", DIR.IN, HDLPrimitiveType.genBitType(), EnumSet.of(HDLPort.OPTION.EXPORT));
    newPort("ext_resetv", DIR.IN, HDLPrimitiveType.genBitType(), EnumSet.of(HDLPort.OPTION.EXPORT));

    newPort("ext_color", DIR.OUT, HDLPrimitiveType.genSignedType(32), EnumSet.of(HDLPort.OPTION.EXPORT));
    newPort("ext_count_h", DIR.IN, HDLPrimitiveType.genSignedType(32), EnumSet.of(HDLPort.OPTION.EXPORT));
    newPort("ext_count_v", DIR.IN, HDLPrimitiveType.genSignedType(32), EnumSet.of(HDLPort.OPTION.EXPORT));
  }
}

de0-cv/de0_cv_start.v : トップモジュール
/*
  Copyright (c) 2016, miya
  All rights reserved.

  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/


module de0_cv_start
  (
   input        CLOCK_50,
   input        RESET_N,
   output       VGA_HS,
   output       VGA_VS,
   output [3:0] VGA_R,
   output [3:0] VGA_G,
   output [3:0] VGA_B
   );

  localparam PLANES = 9;

  wire          clk_video;
  wire          pll_locked;

  // truncate RGB data
  wire [7:0]    VGA_R_in;
  wire [7:0]    VGA_G_in;
  wire [7:0]    VGA_B_in;
  assign VGA_R = VGA_R_in[3:0];
  assign VGA_G = VGA_G_in[3:0];
  assign VGA_B = VGA_B_in[3:0];

  // generate reset signal
  reg           reset;
  reg           reset1;
  reg           resetv;
  reg           resetv1;
  reg           resetpll;
  reg           resetpll1;

  always @(posedge CLOCK_50)
    begin
      resetpll1 <= ~RESET_N;
      resetpll <= resetpll1;
    end

  always @(posedge CLOCK_50)
    begin
      reset1 <= ~pll_locked;
      reset <= reset1;
    end

  always @(posedge clk_video)
    begin
      resetv1 <= ~pll_locked;
      resetv <= resetv1;
    end

  av_pll av_pll_0
    (
     .refclk (CLOCK_50),
     .rst (resetpll),
     .outclk_0 (clk_video),
     .outclk_1 (),
     .locked (pll_locked)
     );

  // connector
  wire signed [32-1:0] count_h;
  wire signed [32-1:0] count_v;
  wire signed [32-1:0] color_all;
  wire signed [32-1:0] color [0:PLANES-1];
  reg signed [32-1:0]  layer [0:PLANES-1];
  integer              i;
  always @*
    begin
      layer[0] = (color[0] == 0) ? 0 : color[0];
      for (i = 0; i < PLANES-1; i = i + 1)
        begin
          layer[i+1] = (color[i+1] == 0) ? layer[i] : color[i+1];
        end
    end
  assign color_all = layer[PLANES-1]; // color_all: delay 8

  SpriteExample SpriteExample_0
    (
     .clk (CLOCK_50),
     .reset (reset),
     .video_vga_ext_clkv_exp (clk_video),
     .video_vga_ext_resetv_exp (resetv),
     .video_vga_ext_color_exp (color_all),
     .video_vga_ext_vga_hs_exp (VGA_HS),
     .video_vga_ext_vga_vs_exp (VGA_VS),
     .video_vga_ext_vga_de_exp (),
     .video_vga_ext_vga_r_exp (VGA_R_in),
     .video_vga_ext_vga_g_exp (VGA_G_in),
     .video_vga_ext_vga_b_exp (VGA_B_in),
     .video_vga_ext_count_h_exp (count_h),
     .video_vga_ext_count_v_exp (count_v),

     .video_sprite0_ext_clkv_exp (clk_video),
     .video_sprite0_ext_resetv_exp (resetv),
     .video_sprite0_ext_color_exp (color[2]),
     .video_sprite0_ext_count_h_exp (count_h),
     .video_sprite0_ext_count_v_exp (count_v),

     .video_sprite1_ext_clkv_exp (clk_video),
     .video_sprite1_ext_resetv_exp (resetv),
     .video_sprite1_ext_color_exp (color[3]),
     .video_sprite1_ext_count_h_exp (count_h),
     .video_sprite1_ext_count_v_exp (count_v),

     .video_sprite2_ext_clkv_exp (clk_video),
     .video_sprite2_ext_resetv_exp (resetv),
     .video_sprite2_ext_color_exp (color[4]),
     .video_sprite2_ext_count_h_exp (count_h),
     .video_sprite2_ext_count_v_exp (count_v),

     .video_sprite3_ext_clkv_exp (clk_video),
     .video_sprite3_ext_resetv_exp (resetv),
     .video_sprite3_ext_color_exp (color[5]),
     .video_sprite3_ext_count_h_exp (count_h),
     .video_sprite3_ext_count_v_exp (count_v),

     .video_sprite4_ext_clkv_exp (clk_video),
     .video_sprite4_ext_resetv_exp (resetv),
     .video_sprite4_ext_color_exp (color[6]),
     .video_sprite4_ext_count_h_exp (count_h),
     .video_sprite4_ext_count_v_exp (count_v),

     .video_sprite5_ext_clkv_exp (clk_video),
     .video_sprite5_ext_resetv_exp (resetv),
     .video_sprite5_ext_color_exp (color[7]),
     .video_sprite5_ext_count_h_exp (count_h),
     .video_sprite5_ext_count_v_exp (count_v),

     .video_sprite6_ext_clkv_exp (clk_video),
     .video_sprite6_ext_resetv_exp (resetv),
     .video_sprite6_ext_color_exp (color[8]),
     .video_sprite6_ext_count_h_exp (count_h),
     .video_sprite6_ext_count_v_exp (count_v),

     .video_spt0_sprite_ext_clkv_exp (clk_video),
     .video_spt0_sprite_ext_resetv_exp (resetv),
     .video_spt0_sprite_ext_color_exp (color[0]),
     .video_spt0_sprite_ext_count_h_exp (count_h),
     .video_spt0_sprite_ext_count_v_exp (count_v),

     .video_bgt0_bg0_ext_clkv_exp (clk_video),
     .video_bgt0_bg0_ext_resetv_exp (resetv),
     .video_bgt0_bg0_ext_color_exp (color[1]),
     .video_bgt0_bg0_ext_count_h_exp (count_h),
     .video_bgt0_bg0_ext_count_v_exp (count_v)
     );

endmodule