Author Topic: FPGA output not consistent with ModelSim  (Read 8386 times)

0 Members and 2 Guests are viewing this topic.

Offline kpow8050Topic starter

  • Regular Contributor
  • *
  • Posts: 57
  • Country: au
Re: FPGA output not consistent with ModelSim
« Reply #25 on: March 21, 2022, 10:34:23 pm »
Awesome, that would be great @BrianHG if you could help me with this :). I really appreciate your offer. Knowing how to properly code in HDL will definitely be an asset for me in the future too. I have attached the current workbench I'm using to test my code. It currently does not test the command interface via the shift register, so instead I directly write to a register through an input. I have also attached the associated module files. All are contained in the .zip file below

Cheers,
Keith
« Last Edit: March 21, 2022, 10:44:04 pm by kpow8050 »
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8134
  • Country: ca
Re: FPGA output not consistent with ModelSim
« Reply #26 on: March 22, 2022, 12:44:59 am »
Ok, here we go.
I've done a basic setup for you.
I've tested it in an old ModelSim 10.1.
There is an annoying bug in 10.1 where if you quick recompile your design more than 50-100 consecutive times, you may need to quit, then rerun Modelsim.  The new Modelsims don't have the bug, but since you are using Cyclone II, I assumed you were on an old modelsim.

Step 1, unzip the LaserControl_v2 into it's own folder.
Step 2, open Modelsim on it's own, no need for Quartus.
Step 3, in Modelsim, Select 'File' and 'Change Directory' and navigate to the LaserControl_v2 folder.
Step 4, in Modelsim's transcript window, type:
'do setup_lc.do'

When first opening Modelsim, you need to do these 4 steps to setup the modelsim.

Every time you make a change in the HDL code, you then just type in the transcript window:
'do run_lc.do'

This does a quick re-compile and re-run.

Ok, for the code.  I did a basic setup for you showing you how to begin.

In the 'LaserControl_v2.sv', I:

Made a dummy override reset.
Then a multi-function case statement which basically keeps the laser off,
then waits for 'in_a' to rise,
then oscillates the laser at 12.5MHz until 'in_a' falls, then back to waiting.

I left 2 blank 'TASK's for setting and running your laser pixel output generator which we will work on creating.

Then we will look at loading controls and adding a line of memory.

In the 'LaserControl_v2_tb.sv', I:

Made the clock an authentic 25MHz.
According to your scope-shot, I make the in_a pulse 250us, or 250000ns and the off time 2250000ns.
However, this is positive logic and your scopeshot shows negative logic.  We would need to fix this if your hardware is negative logic as we want to simulate reality.

I also added an autostop 'ENDTIME' to the sim.

Look around first then lets take a look at how to proceed.
« Last Edit: March 22, 2022, 12:54:27 am by BrianHG »
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8134
  • Country: ca
Re: FPGA output not consistent with ModelSim
« Reply #27 on: March 22, 2022, 12:52:15 am »
I've attached the modelsim output from the above code.

I guess we should start with, is the 'in_a' reference stimuli correct?
Does it match what your scope sees you feeding the FPGA input?
Or is it inverted?

What does 'in_b' do?

Do not worry about the ld_out yet.  I just made cheap dummy code there.
 

Offline kpow8050Topic starter

  • Regular Contributor
  • *
  • Posts: 57
  • Country: au
Re: FPGA output not consistent with ModelSim
« Reply #28 on: March 22, 2022, 01:14:31 am »
What does 'in_b' do? This is actually part of the debouncing for the photodiode. in_a and in_b are from a resistor divider network, which is then feeding into a debounce circuit consisting of a flip flop and some combinational logic to clean up the pulse. Without debouncing the pulse is very messy, impossible to trigger off.
 

Offline kpow8050Topic starter

  • Regular Contributor
  • *
  • Posts: 57
  • Country: au
Re: FPGA output not consistent with ModelSim
« Reply #29 on: March 22, 2022, 01:20:31 am »
So essential IN_A and IN_B are part of a schmitt trigger circuit which provides some hysteresis for the photodiode pulse (since its super messy and has a bunch of intermediate transitions). I have attached the topology of the circuit below. I would prefer to keep this block as is, if possible since it works. But if there are better ways to debounce I'm also open to those

 

Offline kpow8050Topic starter

  • Regular Contributor
  • *
  • Posts: 57
  • Country: au
Re: FPGA output not consistent with ModelSim
« Reply #30 on: March 22, 2022, 01:24:23 am »
I've attached the modelsim output from the above code.

I guess we should start with, is the 'in_a' reference stimuli correct?
Does it match what your scope sees you feeding the FPGA input?
Or is it inverted?

What does 'in_b' do?

Do not worry about the ld_out yet.  I just made cheap dummy code there.

Also based on the timing diagram ld_out should only be active on the low side of the in_a (after the pulse essentially), but I assume this is still being worked on. I will attach a drawing soon showing what I intend the signals to look like if that will help. Also in_a cannot be used directly to trigger off, as it needs to be de-bounced (see my previous post). The signal I'm using to trigger off is a conversion of in_a and in_b after a schmitt trigger, the output is called "SCHMITT_OUT_INV"
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8134
  • Country: ca
Re: FPGA output not consistent with ModelSim
« Reply #31 on: March 22, 2022, 01:35:51 am »
So essential IN_A and IN_B are part of a schmitt trigger circuit which provides some hysteresis for the photodiode pulse (since its super messy and has a bunch of intermediate transitions). I have attached the topology of the circuit below. I would prefer to keep this block as is, if possible since it works. But if there are better ways to debounce I'm also open to those

(Attachment Link)
Remember everything coming in boils down to a clocked output, so, we need to create the equivalent to your circuit, but, clocked right at the beginning.

( Damn, whats with the inverting....)

Will this work (Synchronous version):

Code: [Select]
reg sync_in = 0 ;
always @(posedge clk_in) begin  // remember, we are always operating inside here:
        if (in_a && in_b) sync_in <= 1'b1; // Both high, so set.
  else if !(in_a || in_b) sync_in <= 1'b0; // Both low, so clear, otherwise, hold the last value.
end

Note that there are other ways to debounce as well, like looking for a clean train of 2-10 sequential solid single values as well as the possibility of combining it with what you have above.  However, it looks like you are sampling an analog signal.

Unless you show me the true signal, I bet you don't need such a circuit.  All we need is a single clean transition event.  We do not care that there is noise once switched, we can ignore the trailing junk.
« Last Edit: March 22, 2022, 01:39:53 am by BrianHG »
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8134
  • Country: ca
Re: FPGA output not consistent with ModelSim
« Reply #32 on: March 22, 2022, 01:41:28 am »
With optics, I usually trigger on the 'strike' edge and ignore the after noise.  However, I did not create your hardware.

Step 1, fix the stimulation in the test-bench setup to reflect as close to reality as possible.
Step 2, make the laser perform and adjustable pattern.

Ok, lets see you fix the 'LaserControl_v2_tb.sv' so that 'in_a' and 'in_b' are closer to reality.

Remember, being truthful here means the FPGA will do exactly what we see when we run the code in hardware.
« Last Edit: March 22, 2022, 01:46:33 am by BrianHG »
 

Offline kpow8050Topic starter

  • Regular Contributor
  • *
  • Posts: 57
  • Country: au
Re: FPGA output not consistent with ModelSim
« Reply #33 on: March 22, 2022, 01:46:17 am »
The synchronous version looks like it should work. I can test it later on the actual hardware, but the logic is the same so I don't see why it wouldn't. I will post the drawing of the signals in a sec
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8134
  • Country: ca
Re: FPGA output not consistent with ModelSim
« Reply #34 on: March 22, 2022, 01:52:39 am »
Do you have a drawing of what you want the output to look like?
 

Offline kpow8050Topic starter

  • Regular Contributor
  • *
  • Posts: 57
  • Country: au
Re: FPGA output not consistent with ModelSim
« Reply #35 on: March 22, 2022, 02:00:35 am »
Do you have a drawing of what you want the output to look like?

Yes. Just finished it. I have attached it below
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8134
  • Country: ca
Re: FPGA output not consistent with ModelSim
« Reply #36 on: March 22, 2022, 02:11:17 am »
Do you have a drawing of what you want the output to look like?

Yes. Just finished it. I have attached it below
Ok, easy as pie.

Only from experience with optical triggers, I bet locking onto the rise of the 'trig pulse' and adding a long programmable delay through the pulse until after, then running the laser will have a cleaner function.

The only reason for this is that usually when you get to see the pulse, it is the light hitting the photo diode having a cleaner edge instead of the release side where what you see is the photodiode's pull-down resistor.

It is up to you.

Ok, first fix lines 50-55 in the testbench source file to match what you have drawn with in_a and in_b pulsing with a small delay between them.

Next, you will add my code replacing lines 41 and 42 in the source HDL, and making a second delayed reg 'sync_in' to trigger off of the transition edge of your choice.

Test, and post your resulting code here.
Once ok, we will construct your programmable period pixel timer.
 

Offline kpow8050Topic starter

  • Regular Contributor
  • *
  • Posts: 57
  • Country: au
Re: FPGA output not consistent with ModelSim
« Reply #37 on: March 22, 2022, 02:44:12 am »
Quote
Ok, first fix lines 50-55 in the testbench source file to match what you have drawn with in_a and in_b pulsing with a small delay between them.

Next, you will add my code replacing lines 41 and 42 in the source HDL, and making a second delayed reg 'sync_in' to trigger off of the transition edge of your choice.

Test, and post your resulting code here.
Once ok, we will construct your programmable period pixel timer.

Alright, I will get to it and post once I have finished
 

Offline kpow8050Topic starter

  • Regular Contributor
  • *
  • Posts: 57
  • Country: au
Re: FPGA output not consistent with ModelSim
« Reply #38 on: March 22, 2022, 05:20:58 am »
So I have updated your code to include the schmitt trigger for in_a and in_b, which will convert that into sync_in. I have also added a counter to keep track of how many pixels are output (since I want this to be adjustable). Its working as I expect, although when the pixel count is set to 20 I'm only getting 10 pixels out. I suspect thats because its counting clk_in pulses rather than actual divided output pulses. I have attached a screenshot of the output waveforms below and the code in a zip folder
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8134
  • Country: ca
Re: FPGA output not consistent with ModelSim
« Reply #39 on: March 22, 2022, 09:00:47 am »
Ok, so far, so good.

Some bugs:

line 69:
  else if (!(reg_in_a || reg_in_a)) sync_in <= 1'b0; // Both...

The red reg_in_a should be a 'b'.

Now, the issue with line 88:
 if ( sync_in && !reg_in_a ) function_pos <= 2 ; // When...

Remember, sync_in is filtered, but not reg_in_a.  A good idea is to add a line 70:
       sync_in_dly <= sync_in ;

We are making a transition detector here, IE seeing what happened since the previous clock before and after.
Now, to help simplify your ' if () ' statements everywhere, at around line 32, let's add these 3 'wires'.

Code: [Select]
wire sync_pos_trigger = sync_in && !sync_in_dly ;
wire sync_neg_trigger = !sync_in && sync_in_dly ;
wire sync_trigger_on    = USE_NEG_EDGE ? sync_neg_trigger  : sync_pos_trigger ; // Use a parameter or reg to select the trigger edge.

And at the top of your code , add this parameter:
parameter  bit  USE_NEG_EDGE  = 1 ;

Now back to line 88:  (now probably line ~92)
            if ( sync_trigger_on ) function_pos <= 2 ; // When

---------------------------

Ok, next part, counters, timers and what the 'TASK' does.

The Verilog 'TASK' is akin to a function call, but behaves a bit more like a macro function.

We typically use if for when we want to repeatedly issue a bunch of the same commands as the result of different 'if()' commands, or inside multiple 'case' statement positions.

Here is the beginning of what I have in mind (go down to line approximately 125):
Code: [Select]
// **************************************************
// TASKS - set laser pixel clock and address pointer.
// **************************************************
task SET_laser_clock(bit [15:0] period,bit [15:0] length);
  begin

    pixel_count  <= length ;
    pixel_period <= period ;

  end
endtask

// **************************************************
// TASKS - run laser pixel timer and address pointer.
// **************************************************
task RUN_laser_clock(bit [15:0] period);
  begin

  if (pixel_count !=0) pixel_count   <= pixel_count  - 1'b1  ; // Count down until 0
  else                 function_pos  <= function_pos + 1'b1  ; // Once 0, auto-increment the function_pos for the case statement.

  end
endtask

(Note that I have decided to use 16bit counters since you mentioned 2000 pixels and I figured you might want additional room if you expand the system clock to 100MHz or 200MHz for ultra precision.)

Now, in case function_pos = 1, add:

     SET_laser_clock ( pixel_period_width_reg, pixel_offset_reg );

Now, in case function_pos = 2, remove your counter, in fact remove everything and just place:

     RUN_laser_clock ( pixel_period_width_reg );

As you can see from my RUN_laser_clock task, once the countdown reaches 0, the case function_pos will increment by 1 to become 3.

Lets see if you can work out the case function_pos = 3,4,5...

In your sim output, it may be useful to add to the waveforms the following:

Code: [Select]
add wave -divider     "LaserControl Internals"
add wave -unsigned    sim:/LaserControl_v2_tb/UUT/function_pos
Adding these lines to line 15 of the 'setup_lc.do' will automatically place them in your waveform, in the order you list them, every time you begin and do the first setup.

I would also add the new nets:
sync_trigger_on
pixel_count

Let's see if you can get the code up to the next stage with the pixel offset and pixel count.
Update your code and sim.
Next, we will improve the 'RUN_laser_clock' to have a pixel period, or pixel width.
« Last Edit: March 22, 2022, 09:13:30 am by BrianHG »
 

Offline kpow8050Topic starter

  • Regular Contributor
  • *
  • Posts: 57
  • Country: au
Re: FPGA output not consistent with ModelSim
« Reply #40 on: March 23, 2022, 09:44:41 am »
Sorry for the delay. Been busy with work. I have added the functions for pixel offset and pixel count into the task construct as you described. I have also made some small modifications, rather than counting down I have changed it to count up as this is more intuitive for me. The output waveforms are as expected. One thing I couldn't figure out however was to make the laser remain on at all times, other than when it is oscillating. The reason the laser must remain on is for a sync pulse to be generated requires laser light. So without the laser being on initially there will never be a sync pulse and the events will never trigger. I have included the updated code below and attached a screenshot of the waveform outputs showing a pixel offset (delay from sync pulse) and finite number of pixels output.

Code: [Select]
module LaserControl_v2 (
input data_in,
input comms_clk,
input execute,
input clk_in,
input in_a,
input in_b,
output reg ld_out,
output wire [7:0] debug_reg,
output reg debug_output);

// Control constants
//localparam laser_ctrl_mode = 7;
//localparam set_pixel_offset_mode = 1;
//localparam set_pixel_clock_divider_mode = 5;
//localparam set_pixel_scan_line_limit_mode = 6;
parameter  bit  USE_NEG_EDGE  = 1 ;

reg       reset             = 1 ; // since you do not have a reset, we are artificially making preset to on.
reg       enable_laser      = 0 ; // A reg to block the laser from coming on.
reg       laser_out         = 0 ; // A reg to block the laser from coming on.
reg [4:0] function_pos      = 0 ; // Create a program position pointer which can serve 32 states.


reg        reg_data_in       = 0 ; // Register inputs
reg        reg_comms_clk     = 0 ;
reg        reg_execute       = 0 ;
reg        reg_in_a          = 0 ;
reg        reg_in_b          = 0 ;
reg        sync_in           = 0 ;

  // Internal registers for keeping track of state
reg [15:0] max_pixel_count   = 20;
reg [15:0] pixel_offset      = 100;   // how many pixels to delay the output laser pulses
reg [15:0] pixel_offset_count= 0 ;
reg [15:0] pixel_count       = 0 ;
reg [15:0] pixel_period      = 1 ;   // Period of the pixel clock (1 = period of main clock, 2 = 1/2 of main clock, etc..
reg        sync_in_dly       = 0 ;


wire sync_pos_trigger = sync_in && !sync_in_dly ;
wire sync_neg_trigger = !sync_in && sync_in_dly ;
wire sync_trigger_on  = USE_NEG_EDGE ? sync_neg_trigger  : sync_pos_trigger ; // Use a parameter or reg to select the trigger edge.
 

// **************************************************
// Everything should run from the 25MHz clock.
// **************************************************
always @(posedge clk_in) begin

// Cleanly register FPGA inputs.
reg_data_in    <= data_in   ;
reg_comms_clk  <= comms_clk ;
reg_execute    <= execute   ;
reg_in_a       <= in_a      ;
reg_in_b       <= in_b      ;

// **************************************************
// During the reset condition, do these things.
// **************************************************
if (reset) begin

    reset        <= 0 ; // disable the reset.
    enable_laser <= 0 ; // Force laser off.
    ld_out       <= 0 ; // Force laser off.
    function_pos <= 0 ; // Clear the function position so that position 0 runs once the reset is released.

  end else begin
// **************************************************
// Once the reset condition has cleared, do these things.
// **************************************************

ld_out <= enable_laser && laser_out ; // For now, always drive the laser output pin when these are high and !reset.
                                      // We will change this to a memory lookup when we create the 2048 pixel line memory


// **************************************************
// Schmitt trigger
// **************************************************
        if (reg_in_a && reg_in_b) sync_in <= 1'b1; // Both high, so set.
  else if (!(reg_in_a || reg_in_b)) sync_in <= 1'b0; // Both low, so clear, otherwise, hold the last value.
 
  sync_in_dly <= sync_in;

// **************************************************
// Begin Sequencer driving for the laser output
// **************************************************
     case (function_pos)

        default : function_pos <= 0 ; // If an undefined position has been reached, goto position 0.

       0 : begin // This state begins after the reset has been released

            enable_laser <= 1 ; // Force laser on (default should be on, so that photodiode has a signal to sync).
            function_pos <= 1 ; // Go to position 1.
       
           end

       1 : begin

            if ( sync_trigger_on ) function_pos <= 2 ; // Goto function 2 when the sync_trigger goes from high to low
                                                         // Otherwise stay here doing nothing.
              SET_laser_clock ( pixel_period, max_pixel_count );
           end

       2 : begin
         
              RUN_laser_clock ( pixel_period );

           end

      endcase
// **************************************************
// End Sequencer for driving the laser output
// **************************************************


// **************************************************
// External commands input registering
// **************************************************


  end // !reset
end // always @25 MHz clock.


// **************************************************
// TASKS - set laser pixel clock and address pointer.
// **************************************************
task SET_laser_clock(bit [15:0] period,bit [15:0] length);
  begin
    max_pixel_count <= length;
    pixel_period <= period;
  end
endtask

// **************************************************
// TASKS - run laser pixel timer and address pointer.
// **************************************************
task RUN_laser_clock(bit [15:0] period);
  begin
    // Wait for pixel offset delay
    if (pixel_offset_count < pixel_offset) begin
      pixel_offset_count <= pixel_offset_count + 1'b1;
    end else begin
      enable_laser <= 1 ;           // Enable the laser output.
      laser_out    <= !laser_out ;  // Oscillate the laser
   
      if (pixel_count < max_pixel_count) begin
          pixel_count   <= pixel_count  + 1'b1  ; // Count up until we reach the max pixel length
      end else begin
          pixel_count = 1'b0;                     // Reset the pixel counter
          pixel_offset_count = 1'b0;              // Reset the pixel offset counter
        function_pos  <= function_pos + 1'b1  ; // Once pixel count reached max length, auto-increment the function_pos for the case statement.
    end
    end
  end
endtask


endmodule


So for the next step I guess you will help me implement a way to define the pixel period? I decided not to add my own way of doing that as you probably have a more elegant method than what I have in mind. Thanks again for the help so far  :)
 

Offline kpow8050Topic starter

  • Regular Contributor
  • *
  • Posts: 57
  • Country: au
Re: FPGA output not consistent with ModelSim
« Reply #41 on: March 23, 2022, 11:03:00 am »
I made a small change to the code. I've added code to adjust the pixel period using a counter. The following register was added to keep track of the pixel period count

Code: [Select]
reg [15:0] pixel_period_count= 0 ;    // Keeps track of of how many clock pulses are counted for dividing the main clock
And additional logic was added to the RUN_LASER_CLOCK task as follows

Code: [Select]
task RUN_laser_clock(bit [15:0] period);
  begin
    // Wait for pixel offset delay
    if (pixel_offset_count < pixel_offset) begin
      pixel_offset_count <= pixel_offset_count + 1'b1;
    end else begin
      enable_laser <= 1 ;           // Enable the laser output.
     
      if (pixel_period_count < pixel_period ) begin
        pixel_period_count <= pixel_period_count + 1'b1;  // increment the pixel period count
      end else begin
        // Pixel period reached
        pixel_period_count <= 1'b0;   // reset the pixel period counter
        laser_out    <= !laser_out ;  // Oscillate the laser
   
        if (pixel_count < max_pixel_count) begin
          pixel_count   <= pixel_count  + 1'b1  ; // Count up until we reach the max pixel length
        end else begin
          pixel_count = 1'b0;                     // Reset the pixel counter
          pixel_offset_count = 1'b0;              // Reset the pixel offset counter
        function_pos  <= function_pos + 1'b1  ; // Once pixel count reached max length, auto-increment the function_pos for the case statement.
       end
    end
    end
  end
endtask

The laser pixel clock period is now adjustable
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8134
  • Country: ca
Re: FPGA output not consistent with ModelSim
« Reply #42 on: March 23, 2022, 02:53:01 pm »
Sorry for the delay. Been busy with work. I have added the functions for pixel offset and pixel count into the task construct as you described.

Ok.
I did not say modify the 'task'.
You went and messed up my fully 'multi' functional timer.

This is what I said:

Ok, so far, so good.

Some bugs:

line 69:
  else if (!(reg_in_a || reg_in_a)) sync_in <= 1'b0; // Both...

The red reg_in_a should be a 'b'.

Now, the issue with line 88:
 if ( sync_in && !reg_in_a ) function_pos <= 2 ; // When...

Remember, sync_in is filtered, but not reg_in_a.  A good idea is to add a line 70:
       sync_in_dly <= sync_in ;

We are making a transition detector here, IE seeing what happened since the previous clock before and after.
Now, to help simplify your ' if () ' statements everywhere, at around line 32, let's add these 3 'wires'.

Code: [Select]
wire sync_pos_trigger = sync_in && !sync_in_dly ;
wire sync_neg_trigger = !sync_in && sync_in_dly ;
wire sync_trigger_on    = USE_NEG_EDGE ? sync_neg_trigger  : sync_pos_trigger ; // Use a parameter or reg to select the trigger edge.

And at the top of your code , add this parameter:
parameter  bit  USE_NEG_EDGE  = 1 ;

Now back to line 88:  (now probably line ~92)
            if ( sync_trigger_on ) function_pos <= 2 ; // When

---------------------------

Ok, next part, counters, timers and what the 'TASK' does.

The Verilog 'TASK' is akin to a function call, but behaves a bit more like a macro function.

We typically use if for when we want to repeatedly issue a bunch of the same commands as the result of different 'if()' commands, or inside multiple 'case' statement positions.

Here is the beginning of what I have in mind (go down to line approximately 125):
Code: [Select]
// **************************************************
// TASKS - set laser pixel clock and address pointer.
// **************************************************
task SET_laser_clock(bit [15:0] period,bit [15:0] length);
  begin

    pixel_count  <= length ;
    pixel_period <= period ;

  end
endtask

// **************************************************
// TASKS - run laser pixel timer and address pointer.
// **************************************************
task RUN_laser_clock(bit [15:0] period);
  begin

  if (pixel_count !=0) pixel_count   <= pixel_count  - 1'b1  ; // Count down until 0
  else                 function_pos  <= function_pos + 1'b1  ; // Once 0, auto-increment the function_pos for the case statement.

  end
endtask

(Note that I have decided to use 16bit counters since you mentioned 2000 pixels and I figured you might want additional room if you expand the system clock to 100MHz or 200MHz for ultra precision.)

Now, in case function_pos = 1, add:

     SET_laser_clock ( pixel_period_width_reg, pixel_offset_reg );

Now, in case function_pos = 2, remove your counter, in fact remove everything and just place:

     RUN_laser_clock ( pixel_period_width_reg );

As you can see from my RUN_laser_clock task, once the countdown reaches 0, the case function_pos will increment by 1 to become 3.

Lets see if you can work out the case function_pos = 3,4,5...

In your sim output, it may be useful to add to the waveforms the following:

Code: [Select]
add wave -divider     "LaserControl Internals"
add wave -unsigned    sim:/LaserControl_v2_tb/UUT/function_pos
Adding these lines to line 15 of the 'setup_lc.do' will automatically place them in your waveform, in the order you list them, every time you begin and do the first setup.

I would also add the new nets:
sync_trigger_on
pixel_count

Let's see if you can get the code up to the next stage with the pixel offset and pixel count.
Update your code and sim.
Next, we will improve the 'RUN_laser_clock' to have a pixel period, or pixel width.

OK, step 1.  Roll back the timer 'TASK's to my above attached code.
Step 2, paste in this new 'case sequencer function_pos = 0,1,2,3,4,5,6.' and fill in the necessary voids I left.

Code: [Select]
// **************************************************
// Begin Sequencer driving for the laser output
// **************************************************
     case (function_pos)

        default : function_pos <= 0 ; // If an undefined position has been reached, goto position 0.

       0 : begin // This state begins after the reset has been released

            enable_laser <= 0 ; // Force laser off.
            function_pos <= 1 ; // Go to position 1.
       
           end

// **********************************************************
// ***** FUNCTION 1 : Wait for first trigger event
// *****              Also make sure laser is forced off
// **********************************************************
       1 : begin

          // hint  - we want to turn off the laser
         //        and only once the sync in event happens, we want to increment the 'function_pos'.

           end

// **********************************************************
// ***** FUNCTION 2 : Set timer to pixel offset
// *****
// **********************************************************
       2 : begin

            // hint  - SET_laser_clock( period_reg, offset_reg  );
            //           also, don't forget to increment the function pos.


           end

// **********************************************************
// ***** FUNCTION 3 : Run pixel timer until it reaches 0
// *****
// **********************************************************
       3 : begin

            // hint  -  RUN_laser_clock( period_reg );
            //             warning, I have already built into the 'RUN_laser_clock' task an increment of the 'function_pos'
           //               once the countdown reaches 0.

           end

// **********************************************************
// ***** FUNCTION 4 : Set actual number of pixels on a line
// *****
// **********************************************************
       4 : begin

            // hint  - SET_laser_clock( period_reg, scanline_reg  );


           end

// **********************************************************
// ***** FUNCTION 5 : Turn on laser and run pixel timer
// *****              until it reaches 0.
// **********************************************************
       5 : begin

            // hint  -  RUN_laser_clock( period_reg );
            // hint  -  enable_laser <= 1

            // also, for visualization in the sim, modulate the 'laser_out' reg.

           end

// **********************************************************
// ***** FUNCTION 6 : Turn off laser and go back to function 1
// *****             
// **********************************************************
       6 : begin

            // hint  -  enable_laser <= 0
           //       set  'function_pos' to 1.

           end


      endcase
// **************************************************
// End Sequencer for driving the laser output
// **************************************************

Get this, and only this working, simulating and upload you new code her so we can see how it is going and we will decide to tackle next.
« Last Edit: March 23, 2022, 02:55:46 pm by BrianHG »
 

Offline kpow8050Topic starter

  • Regular Contributor
  • *
  • Posts: 57
  • Country: au
Re: FPGA output not consistent with ModelSim
« Reply #43 on: March 23, 2022, 08:19:42 pm »
Forgive me for my ignorance, but I'm a bit confused as to what the sequencer is actually supposed to do in the other 3 steps (4-6)? I was under the impression that most of the code would be written within the task itself.
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8134
  • Country: ca
Re: FPGA output not consistent with ModelSim
« Reply #44 on: March 23, 2022, 08:51:41 pm »
Forgive me for my ignorance, but I'm a bit confused as to what the sequencer is actually supposed to do in the other 3 steps (4-6)? I was under the impression that most of the code would be written within the task itself.
The 2 tasks I made are nothing more than a timer.
1 task sets the beginning of the counter.
the other task counts down to 0, and once at 0, it will increment the 'function_pos' register.

The case statement, which can also be equivalent to an:

Code: [Select]
if (finction_pos == 0) begin
   blah, blah, blah.
end else if (function_pos == 1) begin
  blah, blah, blah.
end else ...

I made comments after each possible (function_pos == 0, 1, 2, 3, 4, 5, 6...)

Think about what you want to do.

in position #1,  I wrote in the comment to turn off the laser & once the sync event comes in, IE: 'sync_trigger_on', increment the function_pos reg.

in position 2, I said you should set the universal timer's delay, 'SET_laser_clock' to your 'pixel offset' setting and increment the function_pos.

in position 3, I said to just run the timer, ie the 'RUN_laser_clock'.  Remember, inside run laser clock, once the countdown reaches 0, it will increment the function_pos for you.

in position 4, I said to set the laser clock to your number of pixels you want to display on a line & increment the function_pos.

in position 5, I said to turn on the laser, toggle the laser output for effect, and run the 'RUN_laser_clock' timer remembering that inside run laser clock task, once the countdown reaches 0, it will increment the function_pos for you.

in position 6, I said to turn off the laser and set reg function_pos <= 1, meaning that the next cycle, we will be back at position 1, waiting for the 'sync_trigger_on' before we go to position 2.  This is basically a loop.

Now, in your head, follow these instructions ion order and emulate how this home-made instruction step program will run.  Remember, inside each position start, all the equations, or flip-flop regs are running always at your master 25MHz clock input.

So, when we are in state 1, whatever you type inside that begin ... end, will be operating at 25MHz while the functions or operations inside all the other states are ignored, or frozen.
« Last Edit: March 23, 2022, 08:54:59 pm by BrianHG »
 

Offline kpow8050Topic starter

  • Regular Contributor
  • *
  • Posts: 57
  • Country: au
Re: FPGA output not consistent with ModelSim
« Reply #45 on: March 23, 2022, 11:53:37 pm »
Quote
The 2 tasks I made are nothing more than a timer.
1 task sets the beginning of the counter.
the other task counts down to 0, and once at 0, it will increment the 'function_pos' register.

Ahh, I get it now. Will update the code and upload it soon
 

Offline kpow8050Topic starter

  • Regular Contributor
  • *
  • Posts: 57
  • Country: au
Re: FPGA output not consistent with ModelSim
« Reply #46 on: March 24, 2022, 02:03:30 am »
I have implemented the changes to the code. I have attached a .zip file containing the new code below. The output is working as expected. I have modified the laser toggle to ensure that the laser is always on when not toggling. This is the effect I desire
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8134
  • Country: ca
Re: FPGA output not consistent with ModelSim
« Reply #47 on: March 24, 2022, 09:47:13 am »
Ok, after adding a few nets to Modelsim's waveform and setting the grid to 40ns matching your system clock, looking at my 2 snapshots, you can get an idea of how easy it is to see what your code is doing when it is doing it and how you may add or adapt your required sequence of events.



 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8134
  • Country: ca
Re: FPGA output not consistent with ModelSim
« Reply #48 on: March 24, 2022, 09:53:56 am »
Found mistakes, lines :

SET_laser_clock ( pixel_period, pixel_offset ); // setup the laser clock for a delay (pixel offset delay)
RUN_laser_clock ( pixel_period );   // counting pixel offset delay
SET_laser_clock ( pixel_period, max_pixel_count ); // Set the number of pixels to display in the scan line

'pixel_period' is currently the actual counter for the period.  This means that looking at  line 161 in the code:

// **************************************************
// TASKS - set laser pixel clock and address pointer.
// **************************************************
task SET_laser_clock(bit [15:0] period,bit [15:0] length);
  begin
    pixel_count <= length;
    pixel_period <= period;
    pixel_period_timer_reg <= period ;
  end
endtask

And later, use the new dedicated timer reg for it's purpose.

I have modified the laser toggle to ensure that the laser is always on when not toggling. This is the effect I desire

Careful here, take a look at line 74 in your code:
ld_out <= enable_laser && laser_out ;

Make sure this is the true behavior you want.  Maybe keep my original enable/disable laser control and change this line to:

ld_out <= !(enable_laser && laser_out) ;

or:
ld_out <= (!enable_laser || laser_out) ;

The purpose of that 'enable', at least in my head when I recommended it, was to enable drawing dots from what would eventually be a 2000 pixel line memory.  So, you may consider changing line 74 and keeping my original enable/disable logic.
« Last Edit: March 24, 2022, 10:16:52 am by BrianHG »
 

Online BrianHG

  • Super Contributor
  • ***
  • Posts: 8134
  • Country: ca
Re: FPGA output not consistent with ModelSim
« Reply #49 on: March 24, 2022, 10:01:08 am »
Next, the period.

Looking at task 'RUN_laser_clock', see if you can fill in the missing 'xxxxxx' to make the period work for you.

Code: [Select]
// **************************************************
// TASKS - run laser pixel timer and address pointer.
// **************************************************
task RUN_laser_clock(bit [15:0] period);
  begin
 
  if ( xxxxxxxxx != 0 ) xxxxxxxx ;
  else begin
 
        xxxxxxx ;
       
        if (pixel_count !=0) pixel_count   <= pixel_count  - 1'b1  ; // Count down until 0
        else                 function_pos  <= function_pos + 1'b1  ; // Once 0, auto-increment the function_pos for the case statement.
   
    end
  end
endtask

Next, take a look at line 126:
laser_out <= !laser_out;

Try changing that line so that your laser output matches the lsb of your pixel_count.


Once done and simulated, we will take a look at making your external control interface work.
« Last Edit: March 24, 2022, 10:15:02 am by BrianHG »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf