`timescale 1ns/100ps  // 1 ns time unit, 100 ps resolution
`default_nettype none // Makes it easier to detect typos !

module pixelbuffer;
   reg resetq = 0;
   reg clk = 0;
   always #20.0 clk = !clk; // 25 MHz

   reg [31:0] cycles = 0;
   always @(posedge clk) cycles <= resetq ? cycles + 1 : 0;

   /***************************************************************************/
   // Logic to be tested
   /***************************************************************************/

   reg VGA_HSync;
   reg VGA_VSync;

   reg VGA_Red_0;
   reg VGA_Red_1;
   reg VGA_Red_2;
   reg VGA_Grn_0;
   reg VGA_Grn_1;
   reg VGA_Grn_2;
   reg VGA_Blu_0;
   reg VGA_Blu_1;
   reg VGA_Blu_2;

   reg [10:0] mem_address = 0;
   reg [31:0] mem_wdata = 0;
   reg mem_wstrb = 0;
   reg mem_address_is_char = 0;

   /***************************************************************************/
   // VGA.
   /***************************************************************************/

   wire [9:0] xpos;
   wire [9:0] ypos;
   wire vga_enable, hsync, vsync;

   simple_480p_regs syncgenerator (clk, ~resetq, xpos, ypos, hsync, vsync, vga_enable);

   always @(posedge clk)
   begin
     // The pixel pipeline needs one clock cycle to provide bitmap data after
     // the coordinates are valid, and the sync signals arrive one cycle after coordinates
     // are valid. No further delay necessary here.

     VGA_HSync <= hsync;
     VGA_VSync <= vsync;

     {VGA_Red_2, VGA_Red_1, VGA_Red_0,
      VGA_Grn_2, VGA_Grn_1, VGA_Grn_0,               // Black (required by VGA during blanking)
      VGA_Blu_2, VGA_Blu_1, VGA_Blu_0} <= ~vga_enable ? 9'b000_000_000 : color;

   end

   reg  [23:0] pixels [1023:0]; // Die Sammlung an Pixeln, quasi der Vektorgrafikpuffer.
   reg  [23:0] pixel_rdata;    // Das, was gerade frisch aus dem Grafikpuffer gelesen wurde.
   reg  [23:0] pixel_paint;   // Der Pixel, der als nächstes gemalt werden soll.

   wire [23:0] paint = pixel_rstrobe_delay ? pixel_rdata : pixel_paint; // Bei einem Lesezugriff sind die Pixeldaten gerade "im Flug".
   reg   [8:0] color;

   wire framestart    = vsync & ~VGA_VSync;                  // Einen Rücksetzpuls bei Beginn eines neuen Bildes
   wire pixel_now     = paint[19:0] == {ypos, xpos};       // Der aktuelle Pixel soll nun gezeichnet werden
   wire pixel_rstrobe = ~resetq | framestart | pixel_now; // Ein Pixel muss gelesen werden

   reg pixel_rstrobe_delay = 0;

   reg  [9:0] currentpixel = 0; // Die Adresse des aktuellen Pixels der Grafikeinheit
   wire [9:0] nextpixel = ~resetq | framestart ? {~doublebuffer, 9'b0} : currentpixel + 1; // Die Adresse des nächsten Pixels der Grafikeinheit
   wire [9:0] pixeladdr = pixel_rstrobe ? nextpixel : {doublebuffer, mem_address[10:2]};

   reg doublebuffer = 0;

   always @(posedge clk) // Pixel pipeline.
   begin
     if (mem_wstrb & mem_address_is_char) pixels[{doublebuffer, mem_address[10:2]}] <= mem_wdata[23:0];
     pixel_rdata <= pixels[pixeladdr];
     pixel_rstrobe_delay <= pixel_rstrobe;
     if (pixel_rstrobe) currentpixel <= nextpixel;
     if (pixel_rstrobe_delay) pixel_paint <= pixel_rdata;
     color <= pixel_now ? { {3{paint[22]}}, {3{paint[21]}}, {3{paint[20]}} } : 9'b000_000_010;
   end

   wire [23:0] char_rdata = pixel_rdata;
   wire char_rbusy = pixel_rstrobe_delay;

   /***************************************************************************/
   // Test sequence
   /***************************************************************************/

   integer i;
   initial begin
     $dumpfile("pixelbuffer.vcd"); // create a VCD waveform dump
     $dumpvars(0);                // dump variable changes in the testbench
                                 // and all modules under it

     pixels[0] = 24'hF00000;
     pixels[1] = 24'h100005;
     pixels[2] = 24'h500008;
     pixels[3] = 24'h300009;
     pixels[4] = 24'hFFFFFF;

     pixels[512] = 24'h15ABE1;

     mem_address = 0;
     doublebuffer = 1;

     clk = 0;
     resetq = 0;
     @(posedge clk);
     @(posedge clk);
     resetq = 1;

   for (i = 0; i < 2048; i = i + 1) begin
// for (i = 0; i < 800*600; i = i + 1) begin
       @(posedge clk);
   end

     $finish();
   end
endmodule
