Monday, August 8, 2011

Reset Your Expectations

Darryl is moving, so he won't be in today.  Trying to think of things to do while he's gone that won't detract from the juicy, delicious list of things for him to work on this week that I wrote up for him last Friday.  I already completed my "background" task, which was get the time-resolution performance back up to spec.

It might be worth trying to do reset and pause control signals for the high-speed counter; seeing if I can do that without slowing it down.  I think that might work.  The pause control can perhaps be done at the PLL, which is appropriate, because then it will pause the high-speed front end of the input capture datapath as well (whose function isn't meaningful anyway when the clock counter is paused).

I'll need a new PIO called, say, hs_cntr_ctrl (high-speed counter control), say 8 bits wide for now, with bits defined as follows:
  • Bit #0:  HSC_RESET (high-speed counter reset) - When high ('1'), the high-speed time counter value is held at 0 and the high-speed clock signal is paused.  When low ('0'), normal operation is possible.

  • Bit #1:  HSC_RUN_PAUSEn (high-speed counter run/pause) - When high ('1'), the high-speed clock and time counter are freely-running (unless being held in reset state).  When low ('0'), the HS clock is stopped and the time counter does not update (maintains its last value).

  • Bit #2-7:  Reserved for possible future use.
Later, conceivably, we could add more control bits, such as options to dynamically adjust the PLL speed to allow the time resolution to be adaptively optimized in software (trading off speed against reliability as conditions, e.g., environmental temperature, vary).  However, that is a low priority.

Note that new input pulses cannot be captured while the high-speed counter is reset or paused; however, pulses that have already crossed over into the low-speed clock domain of the input capture datapath may continue progressing through the datapath, and can be read out by the processor.

Added hs_cntr_ctrl to SOPC design; set its initial configuration on startup to be 0x01, so that the high-speed counter will be held paused in the reset state (time 0) initially, and the high-speed clock will be paused.  That way, we will not generate any pulse data until the software modifies the control register to commence normal operation.

Regenerating SOPC system generated files; successful.

Added wiring to top-level schematic to break out the HSC_RESET and HSC_RUN_PAUSEn bits from the SOPC system.

Now editing the high-speed PLL (my_pll.v).

Added the "pllena" (enable) input.  Removed the "locked" output, which we are not presently using.  Note to self:  In future, it might be a good idea to pass this output to the software so that it can avoid using the counter when the high-speed clock is not yet locked.  (Right now, we're just assuming that it gets locked quickly enough to avoid causing problems.)

Modified hspeed_counter.v to accept reset and run_pause_n inputs, and pass them to the PLL.  Fed the HSC_RESET and HSC_RUN_PAUSEn bits to the instance in the top-level schematic.

Now we need to modify cs_cntr.vhd to accept the reset/pause inputs.  This is a bit more involved, because we have to modify the register construct it uses (se_reg) as well.  Making changes in a new file, cs_cntr_re.vhd, so that we can revert to the old one easily if necessary.

Modifying se_reg.vhd to accept reset and enable inputs, in a new file se_reg_re.vhd.

And, se_reg_re.vhd in turn uses se_dff.  Making a new version of that as well in se_dff_re.vhd.

Added the new files to the project; now recompiling everything; let's see if it still meets timing constraints.

Hm, for some reason, Quartus doesn't like computing the PLL enable signal combinationally; it wants this signal to come directly from an input pin (of the FPGA, presumably).  I'm not sure what is the reason for this restriction.  But OK, then let's just leave it always on.  The counter can still just ignore it when it is in the reset state, or not enabled.  And, maybe we can gate it before feeding it to the ALTCLKCTRL unit...

OK, fmax is still over 500 MHz.  There are setup/hold violations, but I'm not sure they will matter.  Anyway, let's test it.  First I'll need to modify the C code.  We'll need a new driver module, hscntr_driver.{c,h}.  We'll add these routines (all procedures with void input and output):

  • hscntr_go(); - Make the high-speed counter start (or continue) running.
  • hscntr_pause(); - Pause the high-speed counter.
  • hscntr_reset(); - Reset the high-speed counter and hold it at zero.
  • hscntr_restart(); - Restart the high-speed counter at zero, but let it continue running right away.
First, regenerating the BSP makefiles and rebuilding the BSP project.  (This is necessary to add support for the new PIO to the system libraries.)

Oh, first let me try gating the high-speed clock before passing it to the ALTCLKCTRL unit...  This is a quick-and-dirty way to ensure that when the counter isn't counting, the pulse_cap module also isn't trying to update its state.  Hope it doesn't screw up the timing analysis.  Oh look, ALTCLKCTRL has an enable input, just for this purpose... I should use that...  That way it will be sure not to mess up automatic inference of the clocks.

It looks like disabling the PLL wasn't really what I wanted anyway, as it disables the VCO settings as well, and is a global signal over all PLLs in the FPGA.

Oh, I forgot to make the new PIO bits individually settable/clearable.  Let me do that now.

Oops, it looks like using the enable input to ALTCLKCTRL messed up the fmax!  It is 427 MHz now instead of 506 MHz.  Maybe it is lying though?  It could be just worrying about the timing of that signal itself, when it doesn't really need to.  I should test it to see.

Finished writing the new high-speed counter driver module (hscntr_driver.{c,h}) and integrating appropriate reset/go calls into main.c.  Perhaps I should also make commands for them...

Oh, no, now fmax is down to 431 MHz!  Better try it...  Seems to be working reliably anyway...

OK, let's define the new user commands to control the state of the high-speed clock:

  • HSC_RESTART - Restart only the high-speed clock/counter (not the datapaths).
  • HSC_RESET - Reset and hold the high-speed clock/counter.
  • HSC_STOP - Pause the high-speed clock/counter (and thus also the input capture).
  • HSC_GO - Start/resume the high-speed clock/counter.
Done.  NOTE:  We're now down to only 8.5K free memory on the device.  At some point later, as we continue adding features and using more space, we may have to remove some functionality from the C code, or else perhaps recompile everything with debugging turned off.

OK, the command interpreter is accepting these commands, and the stop/go functionality is working, but the counter doesn't seem to be getting reset.  Let's double-check the path for that signal.

Duh, I just realized I'm not actually using the new version of the counter yet!  Fixed that, and recompiling...  Keeping fingers crossed that this doesn't kill our performance...

OK, the fmax estimate from the timing analyzer keeps creeping down (now at 418 MHz), but the system seems to still be working fine at 500 MHz.  It's probably worrying about the setup time of the enable input, but we don't really care about that so much, since normally enable should anyway only go from low to high once, at the start of the program... Even if the counter bits don't all update on the same cycle that first time, it shouldn't affect subsequent operation.

OK, the new commands HSC_STOP, HSC_GO, HSC_RESET, HSC_RESTART all work as expected.  I think it's a good time to take a lunch break (I got here pretty early this morning)...

Back from lunch.  Maybe now it is a good time to re-verify the serial I/O capability, and hook this thing up to the WiFi board/central server app.

Checking speed of the UART.  It is 57,600 baud.

I need to find that +3.3Vdown pin again, to power the level shifter...  JP12 pin 1 works, and pin 2 is GND.

Got nuthin'... Aha, just forgot to insert the null-modem adapter between the board and the level-shifter.

There is some confusion, due to the fact that the Wi-Fi board transmits on pin 2, and receives on pin 3 (DCE mode), while the PC transmits on pin 3, and receives on pin 2 (DTE mode).  And the level-shifter is designed to sit at the DCE side of a connection.  However, I soldered up its extra back connector appropriately for talking to a DTE.  So the way it works is, the null modem adapter always has to be used, because the Tx/Rx pin assignments on one of the two sides of the level shifter always has to be reversed, depending on which side we are "repurposing" as the DCE side.

Serial output is now working again, but serial input isn't...  I'm wondering if it's because of the reduced device drivers... But there isn't enough memory to compile without that option...  I may have to revisit this tomorrow; maybe I will have to turn off the dual input stream feature...

    No comments:

    Post a Comment