`timescale 1ns/1ns

module vidgen_diag
#(
    parameter hactive = 400,
    parameter vactive = 400
)
(
    input           CLK,
    input           ARESET,
    input           DIR, // 0 slopes upper left to lower right, 1 upper right to lower left

    output          VSYNC,
    output          DE, 
    output [7:0]    PIX_DAT
);

//localparam framerate = 30;

localparam max_val = 240;
localparam min_val = 20;
localparam val_step = 5;

// 30 fps = 33.3333ms per frame
// Aiming for a pixel clock of 5.55556 MHz (easy to generate with 50 / 9)
// Total pixels = 550x412
localparam hblank = 150, vblank = 24;
localparam htotal = hactive + hblank, vtotal = vactive + vblank;
localparam vpwid = 4, hpwid = 16; // lines / pixels of pulse width (low sync)
localparam vbp = 6, vfp = (vblank - vbp - vpwid); // back porch - vsync high, before lines start. front porch - vsync high, after lines end
localparam hbp = 16, hfp = (hblank - hbp - hpwid); // back porch - hsync high, before pixels start. front port - hsync high, after pixels end

localparam COL_BITS = $clog2(htotal);
localparam LINE_BITS = $clog2(vtotal);

wire                pixel_clock;        
//reg [3:0]           pixel_clock_count; // used to generate pixel clock
//reg [5:0]           pixel_clock_count; // used to generate pixel clock

reg [LINE_BITS-1:0]           line_count;
reg [COL_BITS-1:0]           column_count;
reg                 internal_vsync;
reg                 internal_hsync;
reg                 internal_vde; // vertical data enable - tells us we're in active lines
reg                 internal_de; // pixel data is valid (active line, active column)
reg                 hsync_r; // for falling edge of hsync
reg                 vsync_r; // same for hsync

reg [7:0]           frame_val;
reg [7:0]           start_val;
reg [7:0]           cur_val;
reg                 frame_dir;
reg                 start_dir;
reg                 cur_dir;




assign pixel_clock = CLK;

// generate hsync and de
always @(posedge pixel_clock or posedge ARESET) begin
    if(ARESET) begin
        column_count <= 'd0;
        internal_hsync <= 1'b0;
        internal_de <= 1'b0;
        hsync_r <= 1'b0;
    end 
    else begin
        if(column_count >= (htotal - 1)) begin
            column_count <= 'd0;
        end else begin
            column_count <= 'd1 + column_count;
        end
        // Hsync is high whenever not in a sync pulse
        if(column_count >= hpwid)
            internal_hsync <= 1'b1;
        else
            internal_hsync <= 1'b0;
        // DE is only high in the active region...after back porch and before front porch
        if(internal_vde && (column_count >= (hpwid + hbp)) && (column_count < (hpwid + hbp + hactive)))
            internal_de <= 1'b1;
        else
            internal_de <= 1'b0;
        hsync_r <= internal_hsync;
    end

end

// generate vsync
always @(posedge pixel_clock or posedge ARESET) begin
    if(ARESET) begin
        line_count <= 'd0;
        internal_vsync <= 1'b0;
        internal_vde <= 1'b0;
        vsync_r <= 1'b0;
    end 
    else begin
        if(!internal_hsync && hsync_r) begin 
            if(line_count >= (vtotal-1)) begin
                line_count <= 'd0;
            end else begin
                line_count <= 'd1 + line_count;
            end
        end
        // Vsync is high whenever we're not in a sync pulse
        if(line_count >= vpwid)
            internal_vsync <= 1'b1;
        else   
            internal_vsync <= 1'b0;
        // Vertical data enable is high after back porch and before front porch
        if((line_count >= (vpwid + vbp)) && (line_count < (vpwid + vbp + vactive)))
            internal_vde <= 1'b1;
        else
            internal_vde <= 1'b0;

        vsync_r <= internal_vsync;
    end

    
end

// slanty lines
always @(posedge pixel_clock or posedge ARESET) begin
    if(ARESET) begin
        frame_val <= min_val;
        frame_dir <= DIR;
        start_val <= min_val;
        start_dir <= DIR;
        cur_dir <= 1'b0;
        cur_val <= min_val;
    end 
    else begin
        
        if(line_count < (vpwid + vbp)) begin
            // vert blanking before video
            // set start_val and start_dir for this frame
            start_dir <= frame_dir;
            start_val <= frame_val;
        end else if(line_count == (vtotal-1)) begin
            // last line of frame
            if(column_count >= (htotal - 1)) begin
                // last pixel of that line
                if((frame_dir == 1'b0) && (frame_val == min_val)) begin
                        frame_dir <= 1'b1;
                        frame_val <= frame_val + val_step;
                end else if((frame_dir == 1'b1) && (frame_val == max_val)) begin
                    frame_dir <= 1'b0;
                    frame_val <= frame_val - val_step;
                end else begin
                    if(frame_dir)   frame_val <= frame_val + val_step;
                    else            frame_val <= frame_val - val_step;
                end
            end
        end else begin
            // regular video processing for the active area
            if(column_count < (hpwid + hbp)) begin
                // blanking before video signal this line
                // set the cur_val and cur_dir to the starting values
                cur_val <= start_val;
                if(DIR)
                    cur_dir <= start_dir;
                else   
                    cur_dir <= ~start_dir;
            end else if(column_count >= (htotal - 1)) begin
                // last pixel (in blank period) of this line
                // increment start_val for next line
                if((start_dir == 1'b0) && (start_val == min_val)) begin
                    start_dir <= 1'b1;
                    start_val <= start_val + val_step;
                end else if((start_dir == 1'b1) && (start_val == max_val)) begin
                    start_dir <= 1'b0;
                    start_val <= start_val - val_step;
                end else begin
                    if(start_dir)   start_val <= start_val + val_step;
                    else            start_val <= start_val - val_step;
                end
            end else begin
                // every pixel, increase or decrease the value by step amount
                if(cur_dir) begin
                    if(cur_val == max_val) begin
                        cur_dir = 1'b0;
                        cur_val <= cur_val - val_step;
                    end else begin
                        cur_val <= cur_val + val_step;
                    end
                end else begin
                    if(cur_val == min_val) begin
                        cur_dir = 1'b1;
                        cur_val <= cur_val + val_step;
                    end else begin
                        cur_val <= cur_val - val_step;
                    end
                end
            end
        end

    end
end

assign PIX_DAT = cur_val;

// Sync outputs
assign DE = internal_de;
assign VSYNC = internal_vsync;

endmodule
