Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VGA-playground verlilog problems. #15

Open
xzores opened this issue Nov 22, 2024 · 2 comments
Open

VGA-playground verlilog problems. #15

xzores opened this issue Nov 22, 2024 · 2 comments

Comments

@xzores
Copy link

xzores commented Nov 22, 2024

I have now tested on 3 different VGA simulation platforms.
My own, which looks good as seen in the first image:
image

this: https://github.com/SamanMohseni/VGA-Simulation as seen in the second image: (Color are different, single only a single color bit is supported.)
image

and VGA-playgrounds as seen in the last image:
image

It seems that is not the screen interpretation of the signals which is broken but rather the simulation, i do not know why this is the case or cause.

It seems that is the multiplication which goes wrong, idk why it would do that VGA-playground when it works other places.

Do you use verilator with -lint-only?

It is a mandlebrot visualizer.

This is the same behavior on the local running version and the online version.

@xzores
Copy link
Author

xzores commented Nov 22, 2024

My verilog code for reference:


/* verilator lint_off UNUSED */
/* verilator lint_off PINMISSING */

module tt_um_vga_example (
	input  wire [7:0] ui_in,    // Dedicated inputs
	output wire [7:0] uo_out,   // Dedicated outputs
	input  wire [7:0] uio_in,   // IOs: Input path
	output wire [7:0] uio_out,  // IOs: Output path
	output wire [7:0] uio_oe,   // IOs: Enable path (active high: 0=input, 1=output)
	input  wire       ena,      // always 1 when the design is powered, so you can ignore it
	input  wire       clk,      // clock
	input  wire       rst_n     // reset_n - low to reset
);
	
	// VGA signals
	wire hsync;
	wire vsync;
	wire [1:0] R;
	wire [1:0] G;
	wire [1:0] B;
	
	// Instantiate the Adder module
	MandlebrotDisplayer displayer (
		.clock(clk),        // Connect clock signal to clk
		.reset(rst_n),     // Connect reset signal, assuming reset_n is active low
		
		//Display controls
		.io_hsync(hsync),
		.io_vsync(vsync),
		.io_R(R),
		.io_G(G),
		.io_B(B)
	);
	
	// TinyVGA PMOD
	assign uo_out  = {hsync, B[0], G[0], R[0], vsync, B[1], G[1], R[1]};
	
	// Unused signals
	assign uio_out = 0;
	assign uio_oe = 8'b00000000;
	
	// List all unused inputs to prevent warnings
	wire _unused = &{ui_in, ena, uio_in, 1'b0};
	
endmodule



// Generated by CIRCT firtool-1.62.0
module VgaSyncGenerator(
  input        clock,
               reset,
  output       io_hsync,
               io_vsync,
               io_display_on,
  output [9:0] io_hpos,
               io_vpos
);

  reg       hsync;
  reg       vsync;
  reg [9:0] hpos;
  reg [9:0] vpos;
  always @(posedge clock) begin
    if (~reset) begin
      hsync <= 1'h0;
      vsync <= 1'h0;
      hpos <= 10'h0;
      vpos <= 10'h0;
    end
    else begin
      hsync <= hpos > 10'h28F & hpos < 10'h2F0;
      vsync <= vpos > 10'h1E9 & vpos < 10'h1EC;
      if (hpos == 10'h31F | ~reset) begin
        hpos <= 10'h0;
        if (vpos == 10'h20C | ~reset)
          vpos <= 10'h0;
        else
          vpos <= vpos + 10'h1;
      end
      else
        hpos <= hpos + 10'h1;
    end
  end // always @(posedge)
  assign io_hsync = hsync;
  assign io_vsync = vsync;
  assign io_display_on = hpos < 10'h280 & vpos < 10'h1E0;
  assign io_hpos = hpos;
  assign io_vpos = vpos;
endmodule

module Fixed_mul(
  input  [15:0] io_a,
                io_b,
  output [15:0] io_res,
  output        io_overflow
);

  wire [31:0] mul_res = {{16{io_a[15]}}, io_a} * {{16{io_b[15]}}, io_b};
  assign io_res = mul_res[27:12];
  assign io_overflow =
    $signed(io_a) > -16'sh1 ^ $signed(io_b) > -16'sh1
      ? mul_res[31:28] != 4'hF
      : (|(mul_res[31:28]));
endmodule

module Complex_mul(
  input  [15:0] io_a_real,
                io_a_imaginary,
                io_b_real,
                io_b_imaginary,
  output [15:0] io_res_real,
                io_res_imaginary,
  output        io_overflow
);

  wire [15:0] _mul_4_io_res;
  wire        _mul_4_io_overflow;
  wire [15:0] _mul_3_io_res;
  wire        _mul_3_io_overflow;
  wire [15:0] _mul_2_io_res;
  wire        _mul_2_io_overflow;
  wire [15:0] _mul_1_io_res;
  wire        _mul_1_io_overflow;
  Fixed_mul mul_1 (
    .io_a        (io_a_real),
    .io_b        (io_b_real),
    .io_res      (_mul_1_io_res),
    .io_overflow (_mul_1_io_overflow)
  );
  Fixed_mul mul_2 (
    .io_a        (io_a_imaginary),
    .io_b        (io_b_imaginary),
    .io_res      (_mul_2_io_res),
    .io_overflow (_mul_2_io_overflow)
  );
  Fixed_mul mul_3 (
    .io_a        (io_a_real),
    .io_b        (io_b_imaginary),
    .io_res      (_mul_3_io_res),
    .io_overflow (_mul_3_io_overflow)
  );
  Fixed_mul mul_4 (
    .io_a        (io_a_imaginary),
    .io_b        (io_b_real),
    .io_res      (_mul_4_io_res),
    .io_overflow (_mul_4_io_overflow)
  );
  assign io_res_real = _mul_1_io_res - _mul_2_io_res;
  assign io_res_imaginary = _mul_3_io_res + _mul_4_io_res;
  assign io_overflow =
    _mul_1_io_overflow | _mul_2_io_overflow | _mul_3_io_overflow | _mul_4_io_overflow;
endmodule

module MandleComb(
  input  [15:0] io_a_coord,
                io_b_coord,
  output [3:0]  io_itterations,
  output        io_converged
);

  wire [15:0] _multipliers_9_io_res_real;
  wire [15:0] _multipliers_9_io_res_imaginary;
  wire        _multipliers_9_io_overflow;
  wire [15:0] _multipliers_8_io_res_real;
  wire [15:0] _multipliers_8_io_res_imaginary;
  wire        _multipliers_8_io_overflow;
  wire [15:0] _multipliers_7_io_res_real;
  wire [15:0] _multipliers_7_io_res_imaginary;
  wire        _multipliers_7_io_overflow;
  wire [15:0] _multipliers_6_io_res_real;
  wire [15:0] _multipliers_6_io_res_imaginary;
  wire        _multipliers_6_io_overflow;
  wire [15:0] _multipliers_5_io_res_real;
  wire [15:0] _multipliers_5_io_res_imaginary;
  wire        _multipliers_5_io_overflow;
  wire [15:0] _multipliers_4_io_res_real;
  wire [15:0] _multipliers_4_io_res_imaginary;
  wire        _multipliers_4_io_overflow;
  wire [15:0] _multipliers_3_io_res_real;
  wire [15:0] _multipliers_3_io_res_imaginary;
  wire        _multipliers_3_io_overflow;
  wire [15:0] _multipliers_2_io_res_real;
  wire [15:0] _multipliers_2_io_res_imaginary;
  wire        _multipliers_2_io_overflow;
  wire [15:0] _multipliers_1_io_res_real;
  wire [15:0] _multipliers_1_io_res_imaginary;
  wire        _multipliers_1_io_overflow;
  wire [15:0] _multipliers_0_io_res_real;
  wire [15:0] _multipliers_0_io_res_imaginary;
  wire        _multipliers_0_io_overflow;
  wire [15:0] _GEN = _multipliers_0_io_res_real + _multipliers_0_io_res_imaginary;
  wire        _GEN_0 =
    _multipliers_0_io_overflow | $signed({{12{_GEN[15]}}, _GEN}) > 28'sh4000;
  wire [15:0] _GEN_1 = _GEN_0 ? 16'h0 : _multipliers_0_io_res_real + io_a_coord;
  wire [15:0] _GEN_2 = _GEN_0 ? 16'h0 : _multipliers_0_io_res_imaginary + io_b_coord;
  wire [15:0] _GEN_3 = _multipliers_1_io_res_real + _multipliers_1_io_res_imaginary;
  wire        enables_1 =
    ~_GEN_0
    & ~(_multipliers_1_io_overflow | $signed({{12{_GEN_3[15]}}, _GEN_3}) > 28'sh4000);
  wire [15:0] _GEN_4 = enables_1 ? _multipliers_1_io_res_real + io_a_coord : 16'h0;
  wire [15:0] _GEN_5 = enables_1 ? _multipliers_1_io_res_imaginary + io_b_coord : 16'h0;
  wire [15:0] _GEN_6 = _multipliers_2_io_res_real + _multipliers_2_io_res_imaginary;
  wire        enables_2 =
    enables_1
    & ~(_multipliers_2_io_overflow | $signed({{12{_GEN_6[15]}}, _GEN_6}) > 28'sh4000);
  wire [15:0] _GEN_7 = enables_2 ? _multipliers_2_io_res_real + io_a_coord : 16'h0;
  wire [15:0] _GEN_8 = enables_2 ? _multipliers_2_io_res_imaginary + io_b_coord : 16'h0;
  wire [15:0] _GEN_9 = _multipliers_3_io_res_real + _multipliers_3_io_res_imaginary;
  wire        enables_3 =
    enables_2
    & ~(_multipliers_3_io_overflow | $signed({{12{_GEN_9[15]}}, _GEN_9}) > 28'sh4000);
  wire [15:0] _GEN_10 = enables_3 ? _multipliers_3_io_res_real + io_a_coord : 16'h0;
  wire [15:0] _GEN_11 = enables_3 ? _multipliers_3_io_res_imaginary + io_b_coord : 16'h0;
  wire [15:0] _GEN_12 = _multipliers_4_io_res_real + _multipliers_4_io_res_imaginary;
  wire        enables_4 =
    enables_3
    & ~(_multipliers_4_io_overflow | $signed({{12{_GEN_12[15]}}, _GEN_12}) > 28'sh4000);
  wire [15:0] _GEN_13 = enables_4 ? _multipliers_4_io_res_real + io_a_coord : 16'h0;
  wire [15:0] _GEN_14 = enables_4 ? _multipliers_4_io_res_imaginary + io_b_coord : 16'h0;
  wire [15:0] _GEN_15 = _multipliers_5_io_res_real + _multipliers_5_io_res_imaginary;
  wire        enables_5 =
    enables_4
    & ~(_multipliers_5_io_overflow | $signed({{12{_GEN_15[15]}}, _GEN_15}) > 28'sh4000);
  wire [15:0] _GEN_16 = enables_5 ? _multipliers_5_io_res_real + io_a_coord : 16'h0;
  wire [15:0] _GEN_17 = enables_5 ? _multipliers_5_io_res_imaginary + io_b_coord : 16'h0;
  wire [15:0] _GEN_18 = _multipliers_6_io_res_real + _multipliers_6_io_res_imaginary;
  wire        enables_6 =
    enables_5
    & ~(_multipliers_6_io_overflow | $signed({{12{_GEN_18[15]}}, _GEN_18}) > 28'sh4000);
  wire [15:0] _GEN_19 = enables_6 ? _multipliers_6_io_res_real + io_a_coord : 16'h0;
  wire [15:0] _GEN_20 = enables_6 ? _multipliers_6_io_res_imaginary + io_b_coord : 16'h0;
  wire [15:0] _GEN_21 = _multipliers_7_io_res_real + _multipliers_7_io_res_imaginary;
  wire        enables_7 =
    enables_6
    & ~(_multipliers_7_io_overflow | $signed({{12{_GEN_21[15]}}, _GEN_21}) > 28'sh4000);
  wire [15:0] _GEN_22 = enables_7 ? _multipliers_7_io_res_real + io_a_coord : 16'h0;
  wire [15:0] _GEN_23 = enables_7 ? _multipliers_7_io_res_imaginary + io_b_coord : 16'h0;
  wire [15:0] _GEN_24 = _multipliers_8_io_res_real + _multipliers_8_io_res_imaginary;
  wire        enables_8 =
    enables_7
    & ~(_multipliers_8_io_overflow | $signed({{12{_GEN_24[15]}}, _GEN_24}) > 28'sh4000);
  wire [15:0] _GEN_25 = enables_8 ? _multipliers_8_io_res_real + io_a_coord : 16'h0;
  wire [15:0] _GEN_26 = enables_8 ? _multipliers_8_io_res_imaginary + io_b_coord : 16'h0;
  wire [15:0] _GEN_27 = _multipliers_9_io_res_real + _multipliers_9_io_res_imaginary;
  wire        enables_9 =
    enables_8
    & ~(_multipliers_9_io_overflow | $signed({{12{_GEN_27[15]}}, _GEN_27}) > 28'sh4000);
  Complex_mul multipliers_0 (
    .io_a_real        (io_a_coord),
    .io_a_imaginary   (io_b_coord),
    .io_b_real        (io_a_coord),
    .io_b_imaginary   (io_b_coord),
    .io_res_real      (_multipliers_0_io_res_real),
    .io_res_imaginary (_multipliers_0_io_res_imaginary),
    .io_overflow      (_multipliers_0_io_overflow)
  );
  Complex_mul multipliers_1 (
    .io_a_real        (_GEN_1),
    .io_a_imaginary   (_GEN_2),
    .io_b_real        (_GEN_1),
    .io_b_imaginary   (_GEN_2),
    .io_res_real      (_multipliers_1_io_res_real),
    .io_res_imaginary (_multipliers_1_io_res_imaginary),
    .io_overflow      (_multipliers_1_io_overflow)
  );
  Complex_mul multipliers_2 (
    .io_a_real        (_GEN_4),
    .io_a_imaginary   (_GEN_5),
    .io_b_real        (_GEN_4),
    .io_b_imaginary   (_GEN_5),
    .io_res_real      (_multipliers_2_io_res_real),
    .io_res_imaginary (_multipliers_2_io_res_imaginary),
    .io_overflow      (_multipliers_2_io_overflow)
  );
  Complex_mul multipliers_3 (
    .io_a_real        (_GEN_7),
    .io_a_imaginary   (_GEN_8),
    .io_b_real        (_GEN_7),
    .io_b_imaginary   (_GEN_8),
    .io_res_real      (_multipliers_3_io_res_real),
    .io_res_imaginary (_multipliers_3_io_res_imaginary),
    .io_overflow      (_multipliers_3_io_overflow)
  );
  Complex_mul multipliers_4 (
    .io_a_real        (_GEN_10),
    .io_a_imaginary   (_GEN_11),
    .io_b_real        (_GEN_10),
    .io_b_imaginary   (_GEN_11),
    .io_res_real      (_multipliers_4_io_res_real),
    .io_res_imaginary (_multipliers_4_io_res_imaginary),
    .io_overflow      (_multipliers_4_io_overflow)
  );
  Complex_mul multipliers_5 (
    .io_a_real        (_GEN_13),
    .io_a_imaginary   (_GEN_14),
    .io_b_real        (_GEN_13),
    .io_b_imaginary   (_GEN_14),
    .io_res_real      (_multipliers_5_io_res_real),
    .io_res_imaginary (_multipliers_5_io_res_imaginary),
    .io_overflow      (_multipliers_5_io_overflow)
  );
  Complex_mul multipliers_6 (
    .io_a_real        (_GEN_16),
    .io_a_imaginary   (_GEN_17),
    .io_b_real        (_GEN_16),
    .io_b_imaginary   (_GEN_17),
    .io_res_real      (_multipliers_6_io_res_real),
    .io_res_imaginary (_multipliers_6_io_res_imaginary),
    .io_overflow      (_multipliers_6_io_overflow)
  );
  Complex_mul multipliers_7 (
    .io_a_real        (_GEN_19),
    .io_a_imaginary   (_GEN_20),
    .io_b_real        (_GEN_19),
    .io_b_imaginary   (_GEN_20),
    .io_res_real      (_multipliers_7_io_res_real),
    .io_res_imaginary (_multipliers_7_io_res_imaginary),
    .io_overflow      (_multipliers_7_io_overflow)
  );
  Complex_mul multipliers_8 (
    .io_a_real        (_GEN_22),
    .io_a_imaginary   (_GEN_23),
    .io_b_real        (_GEN_22),
    .io_b_imaginary   (_GEN_23),
    .io_res_real      (_multipliers_8_io_res_real),
    .io_res_imaginary (_multipliers_8_io_res_imaginary),
    .io_overflow      (_multipliers_8_io_overflow)
  );
  Complex_mul multipliers_9 (
    .io_a_real        (_GEN_25),
    .io_a_imaginary   (_GEN_26),
    .io_b_real        (_GEN_25),
    .io_b_imaginary   (_GEN_26),
    .io_res_real      (_multipliers_9_io_res_real),
    .io_res_imaginary (_multipliers_9_io_res_imaginary),
    .io_overflow      (_multipliers_9_io_overflow)
  );
  assign io_itterations =
    enables_9
      ? 4'hA
      : enables_8
          ? 4'h9
          : enables_7
              ? 4'h8
              : enables_6
                  ? 4'h7
                  : enables_5
                      ? 4'h6
                      : enables_4
                          ? 4'h5
                          : enables_3
                              ? 4'h4
                              : enables_2 ? 4'h3 : enables_1 ? 4'h2 : {3'h0, ~_GEN_0};
  assign io_converged = enables_9;
endmodule

module MandlebrotDisplayer(
  input        clock,
               reset,
  output       io_hsync,
               io_vsync,
  output [9:0] io_screen_hpos,
               io_screen_vpos,
  output [1:0] io_R,
               io_G,
               io_B
);

  wire [3:0]  _mandle_io_itterations;
  wire        _mandle_io_converged;
  wire        _hv_sync_io_display_on;
  wire [9:0]  _hv_sync_io_hpos;
  wire [9:0]  _hv_sync_io_vpos;
  wire [22:0] _vpos_T_5 =
    23'h0 - {{_hv_sync_io_vpos[9], _hv_sync_io_vpos} - 11'hF0, 12'h0};
  wire        _GEN = ~_hv_sync_io_display_on | _mandle_io_converged;
  VgaSyncGenerator hv_sync (
    .clock         (clock),
    .reset         (reset),
    .io_hsync      (io_hsync),
    .io_vsync      (io_vsync),
    .io_display_on (_hv_sync_io_display_on),
    .io_hpos       (_hv_sync_io_hpos),
    .io_vpos       (_hv_sync_io_vpos)
  );
  MandleComb mandle (
    .io_a_coord     ({{_hv_sync_io_hpos[9], _hv_sync_io_hpos} - 11'h140, 5'h0}),
    .io_b_coord     (_vpos_T_5[22:7]),
    .io_itterations (_mandle_io_itterations),
    .io_converged   (_mandle_io_converged)
  );
  assign io_screen_hpos = _hv_sync_io_hpos;
  assign io_screen_vpos = _hv_sync_io_vpos;
  assign io_R = _GEN ? 2'h0 : 2'h3 - _mandle_io_itterations[1:0];
  assign io_G = _GEN ? 2'h0 : 2'h3 - _mandle_io_itterations[3:2];
  assign io_B = _GEN ? 2'h0 : 2'h3;
endmodule

@urish
Copy link
Member

urish commented Nov 22, 2024

This is the piece of code that dumps the image from the simulated program:

vga-playground/src/index.ts

Lines 194 to 211 in f2836e2

waitFor(() => !getVGASignals().hsync);
for (let x = 0; x < 736; x++) {
const offset = (y * 736 + x) * 4;
jmod.tick2(1);
updateAudio();
const { hsync, vsync, r, g, b } = getVGASignals();
if (hsync) {
break;
}
if (vsync) {
break frameLoop;
}
data[offset] = r * 85;
data[offset + 1] = g * 85;
data[offset + 2] = b * 85;
data[offset + 3] = 0xff;
}
waitFor(() => getVGASignals().hsync);

As you can see, it's pretty dumb - whenever it's time to draw a frame, it waits for hsync to go low, before sampling the simulator output for 736 clock cycles (unless hsync suddenly goes high mid line), the waits for hsync to go high again, and repeating this process 520 times per frame (unless vsync goes high mid frame). Finally, it waits for vsync to go high/low again before moving on to the next frame.

As for verilator's args, you can find them here:

'--cc',
'-O3',
'-Wall',
'-Wno-EOFNEWLINE',
'-Wno-DECLFILENAME',
'--x-assign',
'fast',
'--debug-check', // for XML output
'-Isrc/',
'--top-module',
opts.topModule,
...sourceList,

In general, verilator dumps the AST into an XML file, which is then picked up by the simulator. vxmlparser.ts parses the XML file, and then hdlwasm.ts compiles the result into a WebAssembly module. There is also a JS flavor of the simulator (see hdlruntime.ts), which is slower, but will probably be much easier to debug compared with the Web Assembly flavor.

Note that the simulator code is coming from 8bitworkshop, and AFAIK was written by @sehugg, so I'm not very familiar with the inner workings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants