1

Verilog で Adventure(1979) を再作成しようとしていますが、これまでのところ、キャラクターの移動、衝突、およびマップの生成が行われています。マップをモジュールに分割する前はそれほどちらつきませんでしたが、今では常にちらつきます。この問題を調べていたところ、Basys2 ボードのクロックがかなりうるさく、原因である可能性があることがわかりました。ただし、マップをモジュールに入れても、何かを台無しにしない限り、悪化することはありませんでした。何が起こったのですか?

これが私のマップジェネレーターです:

module map_generator(clk_vga, reset, CurrentX, CurrentY, HBlank, VBlank, playerPosX, playerPosY, mapData
);

  input clk_vga;
  input reset;
  input [9:0]CurrentX;
  input [8:0]CurrentY;
  input HBlank;
  input VBlank;
  input [9:0]playerPosX;
  input [8:0]playerPosY;

  output [7:0]mapData;

  reg [7:0]mColor;
  reg [5:0]currentMap = 0;

  wire [7:0]startCastle;

  StartCastle StartCastle(
    .clk_vga(clk_vga),
    .CurrentX(CurrentX),
    .CurrentY(CurrentY),
    .mapData(startCastle)
  );

  always @(posedge clk_vga) begin
    if(reset)begin
      currentMap <= 0;
    end
  end

  always @(posedge clk_vga) begin
    if(HBlank || VBlank) begin
      mColor <= 0;
    end
    else begin
      if(currentMap == 4'b0000) begin
        mColor[7:0] <= startCastle[7:0];
      end
      //Add more maps later
    end
  end

  assign mapData[7:0] = mColor[7:0];

endmodule

startCastle は次のとおりです。

module StartCastle(clk_vga, CurrentX, CurrentY, active, mapData);

  input clk_vga;
  input [9:0]CurrentX;
  input [8:0]CurrentY;
  input active;

  output [7:0]mapData;

  reg [7:0]mColor;

  always @(posedge clk_vga) begin

    if(CurrentY < 40) begin
      mColor[7:0] <= 8'b11100000;
    end
    else if(CurrentX < 40) begin
      mColor[7:0] <= 8'b11100000;
    end
    else if(~(CurrentX < 600)) begin
      mColor[7:0] <= 8'b11100000;
    end
    else if((~(CurrentY < 440) && (CurrentX < 260)) || (~(CurrentY < 440) && ~(CurrentX < 380))) begin
      mColor[7:0] <= 8'b11100000;
    end else
      mColor[7:0] <= 8'b00011100;       
  end

  assign mapData = mColor;
endmodule

トップモジュールに接続されているVGAドライバーは次のとおりです。

module vga_driver(clk_50MHz, vs_vga, hs_vga, RED, GREEN, BLUE, HBLANK, VBLANK, CURX, CURY, COLOR, CLK_DATA, RESET);

  input clk_50MHz;
  output vs_vga;
  output hs_vga;
  output [2:0] RED;
  output [2:0] GREEN;
  output [1:0] BLUE;
  output HBLANK;
  output VBLANK;

  reg VS = 0;
  reg HS = 0;

  input RESET;

  //current client data
  input [7:0] COLOR;
  output CLK_DATA;
  output [9:0] CURX;
  output [8:0] CURY;

  //##### Module constants (http://tinyvga.com/vga-timing/640x480@60Hz)
  parameter HDisplayArea = 640;  // horizontal display area
  parameter HLimit = 800;        // maximum horizontal amount (limit)
  parameter HFrontPorch = 16;    // h. front porch
  parameter HBackPorch = 48;         // h. back porch
  parameter HSyncWidth = 96;         // h. pulse width

  parameter VDisplayArea = 480;  // vertical display area
  parameter VLimit = 525;        // maximum vertical amount (limit)
  parameter VFrontPorch = 10;    // v. front porch
  parameter VBackPorch = 33;         // v. back porch
  parameter VSyncWidth = 2;      // v. pulse width 

  //##### Local variables
  wire clk_25MHz;

  reg [9:0] CurHPos = 0; //maximum of HLimit (2^10 - 1 = 1023)
  reg [9:0] CurVPos = 0; //maximum of VLimit
  reg HBlank_reg, VBlank_reg, Blank = 0;

  reg [9:0] CurrentX = 0;    //maximum of HDisplayArea
  reg [8:0] CurrentY = 0;    //maximum of VDisplayArea (2^9 - 1 = 511)

  //##### Submodule declaration
  clock_divider clk_div(.clk_in(clk_50MHz), .clk_out(clk_25MHz));

  //shifts the clock by half a period (negates it)
  //see timing diagrams for a better understanding of the reason for this
  clock_shift clk_shift(.clk_in(clk_25MHz), .clk_out(CLK_DATA));

  //simulate the vertical and horizontal positions
  always @(posedge clk_25MHz) begin
    if(CurHPos < HLimit-1) begin
      CurHPos <= CurHPos + 1;
    end
    else begin
      CurHPos <= 0;

      if(CurVPos < VLimit-1)
        CurVPos <= CurVPos + 1;
      else
        CurVPos <= 0;
    end
    if(RESET) begin
      CurHPos <= 0;
      CurVPos <= 0;
    end
  end

  //##### VGA Logic (http://tinyvga.com/vga-timing/640x480@60Hz)

  //HSync logic
  always @(posedge clk_25MHz)
    if((CurHPos < HSyncWidth) && ~RESET)
      HS <= 1;
    else
      HS <= 0;

  //VSync logic     
  always @(posedge clk_25MHz)
    if((CurVPos < VSyncWidth) && ~RESET)
      VS <= 1;
    else
      VS <= 0;

//Horizontal logic      
  always @(posedge clk_25MHz) 
    if((CurHPos >= HSyncWidth + HFrontPorch) && (CurHPos < HSyncWidth + HFrontPorch + HDisplayArea) || RESET)
      HBlank_reg <= 0;
    else
      HBlank_reg <= 1;

  //Vertical logic
  always @(posedge clk_25MHz)
    if((CurVPos >= VSyncWidth + VFrontPorch) && (CurVPos < VSyncWidth + VFrontPorch + VDisplayArea) || RESET)
      VBlank_reg <= 0;
    else
      VBlank_reg <= 1;

  //Do not output any color information when we are in the vertical
  //or horizontal blanking areas. Set a boolean to keep track of this.
  always @(posedge clk_25MHz)
    if((HBlank_reg || VBlank_reg) && ~RESET)
      Blank <= 1;
    else
      Blank <= 0;

  //Keep track of the current "real" X position. This is the actual current X
  //pixel location abstracted away from all the timing details
  always @(posedge clk_25MHz)
    if(HBlank_reg && ~RESET)
      CurrentX <= 0;
    else
      CurrentX <= CurHPos - HSyncWidth - HFrontPorch;

  //Keep track of the current "real" Y position. This is the actual current Y
  //pixel location abstracted away from all the timing details
  always @(posedge clk_25MHz) 
    if(VBlank_reg && ~RESET)
      CurrentY <= 0;
    else
      CurrentY <= CurVPos - VSyncWidth - VFrontPorch;

  assign CURX = CurrentX;
  assign CURY = CurrentY;
  assign VBLANK = VBlank_reg;
  assign HBLANK = HBlank_reg;
  assign hs_vga = HS;
  assign vs_vga = VS;

  //Respects VGA Blanking areas
  assign RED = (Blank) ? 3'b000 : COLOR[7:5];
  assign GREEN = (Blank) ? 3'b000 : COLOR[4:2];
  assign BLUE = (Blank) ? 2'b00 : COLOR[1:0];
endmodule

clk_div:

module clock_divider(clk_in, clk_out);
  input clk_in;
  output clk_out;

  reg clk_out = 0;

  always @(posedge clk_in)
    clk_out <= ~clk_out;

endmodule

clk_shift:

module clock_shift(clk_in, clk_out);
  input clk_in;
  output clk_out;

  assign clk_out = ~clk_in;
endmodule
4

1 に答える 1

1

コメントに写真を入れることができないため、これを回答として投稿します。

これはあなたのデザインのように見えますか? OP デザインからの VGA 出力

私の唯一の推測では、ATM はvga_driverand/orのインスタンス化中にいくつかのポートを置き忘れた可能性がありmap_generatorます (古いスタイルのインスタンス化を使用した場合)。それにもかかわらず、hblank 間隔が表示されているかのように、画面の左側に奇妙な縦線が表示されるので、VGA タイミングを確認します。

ところで、表示を生成する方法を変更しました。次のクロック サイクルで更新される HS、VS などに regs を使用します。私は表示生成を FSM として扱っているので、カウンターからの特定の値 (または値の範囲) によってトリガーされる組み合わせブロックから出力が得られます。その上、水平および垂直カウンターを開始して、画面内のピクセル座標で測定された位置 (0,0) が実際に水平および垂直カウンターからの値 (0,0) にマップされるようにするため、演算は必要ありません。

これは、VGA ディスプレイ生成の私のバージョンです。

module videosyncs (
   input wire clk,

   input wire [2:0] rin,
   input wire [2:0] gin,
   input wire [1:0] bin,

   output reg [2:0] rout,
   output reg [2:0] gout,
   output reg [1:0] bout,

   output reg hs,
   output reg vs,

   output wire [10:0] hc,
   output wire [10:0] vc
   );

   /* http://www.abramovbenjamin.net/calc.html */

   // VGA 640x480@60Hz,25MHz
   parameter htotal = 800;
   parameter vtotal = 524;
   parameter hactive = 640;
   parameter vactive = 480;
   parameter hfrontporch = 16;
   parameter hsyncpulse = 96;
   parameter vfrontporch = 11;
   parameter vsyncpulse = 2;
   parameter hsyncpolarity = 0;
   parameter vsyncpolarity = 0;

   reg [10:0] hcont = 0;
   reg [10:0] vcont = 0;
   reg active_area;

    assign hc = hcont;
    assign vc = vcont;

   always @(posedge clk) begin
      if (hcont == htotal-1) begin
         hcont <= 0;
         if (vcont == vtotal-1) begin
            vcont <= 0;
         end
         else begin
            vcont <= vcont + 1;
         end
      end
      else begin
         hcont <= hcont + 1;
      end
   end

   always @* begin
      if (hcont>=0 && hcont<hactive && vcont>=0 && vcont<vactive)
         active_area = 1'b1;
      else
         active_area = 1'b0;
      if (hcont>=(hactive+hfrontporch) && hcont<(hactive+hfrontporch+hsyncpulse))
         hs = hsyncpolarity;
      else
         hs = ~hsyncpolarity;
      if (vcont>=(vactive+vfrontporch) && vcont<(vactive+vfrontporch+vsyncpulse))
         vs = vsyncpolarity;
      else
         vs = ~vsyncpolarity;
    end

   always @* begin
      if (active_area) begin
         gout = gin;
         rout = rin;
         bout = bin;
      end
      else begin
         gout = 3'h00;
         rout = 3'h00;
         bout = 2'h00;
      end
   end
endmodule   

モジュールによってインスタンス化され、vga_driverこのモジュールのラッパーにすぎません。

module vga_driver (
  input wire clk_25MHz,
  output wire vs_vga,
  output wire hs_vga,
  output wire [2:0] RED,
  output wire [2:0] GREEN,
  output wire [1:0] BLUE,
  output wire HBLANK,
  output wire VBLANK,
  output [9:0] CURX,
  output [8:0] CURY,
  input [7:0] COLOR,
  input wire RESET
  );

  assign HBLANK = 0;
  assign VBLANK = 0;

  videosyncs syncgen (
     .clk(clk_25MHz),
     .rin(COLOR[7:5]),
     .gin(COLOR[4:2]),
     .bin(COLOR[1:0]),

     .rout(RED),
     .gout(GREEN),
     .bout(BLUE),

     .hs(hs_vga),
     .vs(vs_vga),

     .hc(CURX),
     .vc(CURY)
   );
endmodule

では、このブロックmap_generatorの最初のifステートメントalwaysが true になることはありません。VGA ディスプレイ モジュールは、必要に応じて RGB 出力をブランクにするため、忘れることができます。

  always @(posedge clk_vga) begin
    if(HBlank || VBlank) begin //
      mColor <= 0;             // Never reached
    end                        //
    else begin                 //
      if(currentMap == 4'b0000) begin
        mColor[7:0] <= startCastle[7:0];
      end
      //Add more maps later
    end
  end

同じアプローチを使用して、マップ ジェネレーター モジュールを組み合わせモジュールに変換しました。たとえば、マップ 0 (城 - 城なし) の場合は次のようになります。

module StartCastle(
  input wire [9:0] CurrentX,
  input wire [8:0] CurrentY,
  output wire [7:0] mapData
  );

  reg [7:0] mColor;
  assign mapData = mColor;

  always @* begin
    if(CurrentY < 40) begin
      mColor[7:0] <= 8'b11100000;
    end
    else if(CurrentX < 40) begin
      mColor[7:0] <= 8'b11100000;
    end
    else if(~(CurrentX < 600)) begin
      mColor[7:0] <= 8'b11100000;
    end
    else if((~(CurrentY < 440) && (CurrentX < 260)) || (~(CurrentY < 440) && ~(CurrentX < 380))) begin
      mColor[7:0] <= 8'b11100000;
    end else
      mColor[7:0] <= 8'b00011100;       
  end
endmodule

出力がピクセルに入る色であるFSMだけです。入力は現在のピクセルの座標です。

したがって、マップ 0 を表示するときは、map_generator は currentMap の現在の値に基づいてマップ 0 に切り替えるだけです。

module map_generator (
  input wire clk,
  input wire reset,
  input wire [9:0]CurrentX,
  input wire [8:0]CurrentY,
  input wire HBlank,
  input wire VBlank,
  input wire [9:0]playerPosX,
  input wire [8:0]playerPosY,
  output wire [7:0]mapData
  );

  reg [7:0] mColor;
  assign mapData = mColor;

  reg [5:0]currentMap = 0;

  wire [7:0] castle_map;
  StartCastle StartCastle(
    .CurrentX(CurrentX),
    .CurrentY(CurrentY),
    .mapData(castle_map)
  );

  always @(posedge clk) begin
    if(reset) begin
      currentMap <= 0;
    end
  end

  always @* begin
    if(currentMap == 6'b000000) begin
      mColor = castle_map;
    end
      //Add more maps later
  end
endmodule

これは、多数のコム ロジックが生成されているように見えるため、グリッチが発生する可能性があります。実際には非常に高速で、画面に目立った不具合はなく、実際の現在の x 座標と y 座標を使用して、画面に表示するものを選択できます。したがって、反転クロックは必要ありません。あなたのデザインの最終バージョンには、25MHz クロックが 1 つしかありません。

ところで、デバイスに依存する構造をデザインから遠ざけて、クロック ジェネレーターなどを、デバイスに依存する唯一のモジュールであるトップ モジュールのデザインに接続される別のモジュールに配置する必要があります。

そこで、ゲーム全体を含む、デバイスに依存しないアドベンチャー モジュールを作成しました。

module adventure (
  input clk_vga,
  input reset,
  output vs_vga,
  output hs_vga,
  output [2:0] RED,
  output [2:0] GREEN,
  output [1:0] BLUE
  );

  wire HBLANK, VBLANK;
  wire [7:0] COLOR;
  wire [9:0] CURX;
  wire [8:0] CURY;
  wire [9:0] playerPosX = 10'd320;  // no actually used in the design yet
  wire [8:0] playerPosY = 9'd240;   // no actually used in the design yet

  vga_driver the_screen (.clk_25MHz(clk_vga), 
                         .vs_vga(vs_vga), 
                         .hs_vga(hs_vga), 
                         .RED(RED), 
                         .GREEN(GREEN), 
                         .BLUE(BLUE), 
                         .HBLANK(HBLANK), 
                         .VBLANK(VBLANK), 
                         .CURX(CURX), 
                         .CURY(CURY), 
                         .COLOR(COLOR)
                         );
  map_generator the_mapper (.clk(clk_vga), 
                            .reset(reset), 
                            .CurrentX(CURX), 
                            .CurrentY(CURY), 
                            .HBlank(HBLANK), 
                            .VBlank(VBLANK), 
                            .playerPosX(playerPosX), 
                            .playerPosY(playerPosY), 
                            .mapData(COLOR)
                            );
endmodule

このモジュールは完全ではありません: プレイヤーの現在位置を更新するためのジョイスティックやその他の入力デバイスからの入力がありません。今のところ、プレイヤーの現在位置は固定されています。

トップ レベル デザイン (TLD) は、使用している FPGA トレーナー専用に記述されています。ここで、Spartan 3/3E デバイスの DCM など、デバイスの使用可能なリソースを使用して適切なクロックを生成する必要があります。

module tld_basys(
  input wire clk_50MHz,
  input wire RESET,
  output wire vs_vga,
  output wire hs_vga,
  output wire [2:0] RED,
  output wire [2:0] GREEN,
  output wire [1:0] BLUE
  );

  wire clk_25MHz;  

  dcm_clocks gen_vga_clock (
                            .CLKIN_IN(clk_50MHz), 
                            .CLKDV_OUT(clk_25MHz)
                           );

  adventure the_game (.clk_vga(clk_25MHz), 
                      .reset(RESET), 
                      .vs_vga(vs_vga), 
                      .hs_vga(hs_vga), 
                      .RED(RED), 
                      .GREEN(GREEN), 
                      .BLUE(BLUE)
                      );
endmodule

DCM で生成されたクロックは、このモジュールに入ります (Xilinx Core Generator で生成)。

module dcm_clocks (CLKIN_IN, 
             CLKDV_OUT
             );

   input CLKIN_IN;
   output CLKDV_OUT;

   wire CLKFB_IN;
   wire CLKFX_BUF;
   wire CLKDV_BUF;
   wire CLKIN_IBUFG;
   wire CLK0_BUF;
   wire GND_BIT;

   assign GND_BIT = 0;
   BUFG  CLKDV_BUFG_INST (.I(CLKDV_BUF), 
                         .O(CLKDV_OUT));                         
   IBUFG  CLKIN_IBUFG_INST (.I(CLKIN_IN), 
                           .O(CLKIN_IBUFG));
   BUFG  CLK0_BUFG_INST (.I(CLK0_BUF), 
                        .O(CLKFB_IN));
   DCM_SP #(.CLKDV_DIVIDE(2.0), .CLKIN_DIVIDE_BY_2("FALSE"), 
         .CLKIN_PERIOD(20.000), .CLKOUT_PHASE_SHIFT("NONE"), 
         .DESKEW_ADJUST("SYSTEM_SYNCHRONOUS"), .DFS_FREQUENCY_MODE("LOW"), 
         .DLL_FREQUENCY_MODE("LOW"), .DUTY_CYCLE_CORRECTION("TRUE"), 
         .FACTORY_JF(16'hC080), .PHASE_SHIFT(0), .STARTUP_WAIT("FALSE") ) 
         DCM_SP_INST (.CLKFB(CLKFB_IN), 
                       .CLKIN(CLKIN_IBUFG), 
                       .DSSEN(GND_BIT), 
                       .PSCLK(GND_BIT), 
                       .PSEN(GND_BIT), 
                       .PSINCDEC(GND_BIT), 
                       .RST(GND_BIT), 
                       .CLKDV(CLKDV_BUF), 
                       .CLKFX(), 
                       .CLKFX180(), 
                       .CLK0(CLK0_BUF), 
                       .CLK2X(), 
                       .CLK2X180(), 
                       .CLK90(), 
                       .CLK180(), 
                       .CLK270(), 
                       .LOCKED(), 
                       .PSDONE(), 
                       .STATUS());
endmodule

(ザイリンクス デバイスの場合)単純なクロック分周器を使用しても安全ですが。シンセサイザーが分周されたクロックを実際のクロックとして処理しないことが懸念される場合は、分周器からの出力をグローバル バッファーにルーティングする BUFG プリミティブを追加して、問題なくクロックとして使用できるようにします (上記のモジュールを参照してください)。これを行う方法の例)。

最後に、グラフィックスに 24 ビット カラーを使用して、最終的なデバイスからの独立性をさらに追加することをお勧めします。TLD では、実際に持っているカラー コンポーネントごとの実際のビット数を使用しますが、Basys2 の 8 ビット カラー トレーナー ボードから、たとえば Nexys4 ボードの 12 ビット カラーに移行すると、自動的により豊かな出力表示をお楽しみください。

これで、次のようになります (左側に縦棒がなくなり、色がより鮮やかになったように見えます)。

別の VGA ディスプレイ モジュールを使用した Basys からの別の出力

于 2015-04-25T09:37:07.490 に答える