Search code examples
verilogsystem-verilogvga

I wish to display a line with a negative slope in system verilog through VGA (positive sloped lines are displayed correctly)


So basically I want my monitor to display a negatively sloped line. The current code which I wrote displays any positively sloped line perfectly however if I attempt to make it negative, it doesn't appear or becomes dotted. I have a vague idea of why it doesn't work however if anyone could point me in the right direction it would be much appreciated.

(Note: My VGA driver works fine as well as the VGAWrapper)

This is the code:

module vga_rgb(
    input logic [8:0] row_o, 
    input logic [9:0] column_o, 
    input logic clk_i,reset_i, 
    output logic [15:0]rgb_i
    );

  localparam X1 = 10'd200; 
  localparam Y1 = 9'd100;

  localparam X2 = 10'd400;
  localparam Y2 = 9'd300;

  wire [15:0] slope = ((Y2-Y1)/(X2-X1));

  always@(posedge clk_i, posedge reset_i)
    if (reset_i) 
      rgb_i <= 16'b0; 
    else 
      if (((row_o-Y1) == (slope * (column_o-X1))) && ((row_o < 300) && (row_o > 100/)))
        rgb_i <= 16'b0;
      else        
        rgb_i <= 16'b11111_111111_11111;

Solution

  • You're almost there. The problem is with a negative slope is, well, that you're dealing with signed quantities now which can be pretty tricky in Verilog.

    The two main rules to keep in mind are:

    1. Be sure to allocate an extra bit for the sign bit. While you can represent 0..1023 in 10b unsigned. That becomes -512..511 when signed.
    2. In verilog the result of a math opreation is unsigned unless both operands are signed. This is usually deeply surprising behavior to newcomers but that's the way the language works.

    So with a few signed annotations, fixing the width of your constants, reducing the output to one bit, and repairing what I presume what was a cut and paste error I end up with this:

    module vga_rgb(
        input wire [8:0] row_o,
        input wire [9:0] column_o,
        input logic clk_i,reset_i,
        output logic rgb_i
        );
    
      localparam X1 = 11'sd200;
      localparam Y1 = 10'sd100;
    
      localparam X2 = 11'sd400;
      localparam Y2 = 10'sd300;
    
      wire signed [15:0] slope = ((Y2-Y1)/(X2-X1));
    
      always@(posedge clk_i, posedge reset_i)
        if (reset_i)
          rgb_i <= 16'b0;
        else
          if ((($signed({1'b0, row_o})-Y1) == (slope * ($signed({1'b0, column_o})-X1))) && ((row_o < Y2) && (row_o > Y1)))
            rgb_i <= 1'b1;
          else
            rgb_i <= 1'b0;
    
    endmodule
    

    A few things to note:

    1. The 's' in the localparams denote a signed constant
    2. The $signed system task which forces a value to be treated as signed. Note how I added an leading zero so if the high bit of the incoming signal is set we don't inadvertently get a negative number.