2015-04-25 4 views
1

Я пытаюсь воссоздать Adventure (1979) в Verilog, и до сих пор у меня есть движение персонажа, коллизия и создание карты. Он не мерцал так сильно, прежде чем я разделил карты на модули, теперь они мерцают постоянно. Когда я искал эту проблему, я узнал, что часы на плате Basys2 довольно шумные и могут быть виновниками. Однако размещение карт в модули не должно было ухудшить ситуацию, если я не испортил что-то. Любая идея, что случилось?My verilog VGA драйвер вызывает мерцание экрана (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/[email protected]) 
    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/[email protected]) 

    //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 
+0

Что ваш модуль clk_div? Это DCM/PLL? –

+0

Я использую Spartan 3E, который, к сожалению, не поддерживает PLL. Я добавил модули clk_shift и clk_div в OP. – Dilancuan

+0

Это может быть ваша проблема. Часы, генерируемые логикой, ** не анализируются с помощью механизма синхронизации, таким образом * могут * тайминга сбоя, не сообщая об этом. Нет причин использовать разделенные часы таким образом. Вместо этого генерируйте сигнал «clock_en» (половина скорости clk) и используйте его в качестве разрешения часов на вашей логике 50 МГц, где бы вы не использовали 'clk_25MHz'. Попробуйте это и сообщите нам, исправляет ли вы вашу проблему.Наблюдая за результатами таймингов, они могут выйти из строя после этого изменения, так как путь будет фактически проанализирован. –

ответ

1

Я отправляю это как ответ, потому что я не могу поставить фотографию в комментарий.

Это ваш дизайн? VGA output from the OP design

Моя только догадываться ATM является то, что вы, возможно, потеряли несколько портов в конкретизации vga_driver и/или map_generator (если вы использовали старый стиль экземпляра). Тем не менее, я собираюсь проверить тимпинг VGA, так как я вижу странную вертикальную линию слева от экрана, как если бы был видимый интервал hblank.

Кстати: я изменил способ создания дисплея. Вы используете regs для HS, VS и т. Д., Которые обновляют следующий такт. Я рассматриваю генерацию отображения как 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 [email protected],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 никогда не будет правдой. Мы можем забыть об этом, так как модуль 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 (замок -Беза замка, я See-) это так:

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

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, чтобы выбрать, что отображать на экране. Таким образом, нет необходимости в перевернутых часах. Моя окончательная версия вашего дизайна имеет только одну тактовую частоту 25 МГц.

Кстати, вы хотите, чтобы конструкции, зависящие от устройства, были отделены от вашего дизайна, размещая такие вещи, как генераторы часов, в отдельных модулях, которые будут подключены к вашему дизайну в верхнем модуле, который должен быть единственным модулем, зависящим от устройства.

Итак, я написал модуль приключенческого аппаратно-агностик, который будет содержать всю игру:

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, который у вас есть. Здесь вы должны генерировать правильные часы, используя доступные ресурсы вашего устройства, такие как DCM в устройствах Spartan 3/3E.

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 

ДХМ генерироваться часы идет в этом модуле (порожденного Core Generator Xilinx)

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 

Хотя это безопасно (для устройств Xilinx) использовать простой делитель тактовой частоты, как вы делали. Если вы опасаетесь, что синтезатор не будет обрабатывать ваши разделенные часы как фактические часы, добавьте примитив BUFG для маршрутизации вывода из делителя в глобальный буфер, чтобы его можно было использовать как часы без проблем (см. Модуль выше для пример о том, как это сделать).

В заключение вы можете добавить дополнительную независимость от конечного устройства, используя 24-битные цвета для вашей графики. В TLD вы будете использовать фактическое количество бит на цветной компонент, который у вас действительно есть, но если вы перейдете с Basys2 на 8-битную плату тренера цвета на, скажем, на плату Nexys4 с 12-битным цветом, вы автоматически наслаждайтесь более богатым дисплеем вывода.

Теперь это выглядит так (без вертикальных полос в левой, и цвета кажутся более яркими)

Another output from Basys using a different VGA display module

+0

Я пробовал ваш модуль videoyncs, и он, по-видимому, исправил проблему по большей части. Я позаботился о движении игрока в верхнем модуле. Все динамические объекты рисуются в верхнем модуле, где все статические объекты рисуются генератором карт. Я по-прежнему относительно новичок в verilog, и я на самом деле делаю это для своего финального проекта в своем классе цифровой логики, поэтому это может показаться исправляющим вопросом, но: В чем разница между @ * и @ (posedge clk)? – Dilancuan

+0

'@ *' используется для определения блока, в котором комбинационная логика описывается с помощью поведенческих операторов. '@ (posedge clk)' используется для определения блока, в котором описана последовательная логика, снова используя поведенческие инструкции. '@ *' - это короткая форма '@ (*)', которая, в свою очередь, является короткой формой '@ (... списка чувствительности ......)' –

Смежные вопросы