JACK WHITHAM    
       
    Home -> Debug Device    

   
 
  On this page:  
 
   
 
  VL2 documentation:  
 
The highest numbered channel provided by vlabifhw is always connected to a debugging device. The debugging device has the following features: The device doesn't come with a GUI debugging frontend. You have to write a Python program to carry out the debugging operations that you want: example. You also have to set up the internal VHDL connections appropriately. In some cases this may be quite a bit of work. However, it will save you time if there is a problem, because it gives you the information you need to find the source of the error.

Related Work

The debugging device has proved invaluable for testing and debugging various hardware components including parts of JOP and a PhD thesis. The key feature is the ability to look inside an FPGA to inspect a design of any complexity. In the early years of computing, it was relatively easy to debug a circuit, i.e. resolve internal design problems, because all of the connections were physically exposed in circuit boards and wiring racks. When integrated circuits became widely used, this type of testing was much more difficult because circuits were tiny and components were sealed inside chips. Integrated circuits such as CPUs are relatively likely to include bugs because of their complexity, but these bugs cannot be easily found due to the inaccessibility of the components.

To solve this problem, many integrated circuits include debugging hardware that is not used during normal operation. It is used only for testing at the factory, and for resolving problems with the design. Such hardware frequently includes a "scan chain": a long shift register that runs near to all of the points of interest in the circuit. At least two signals exist within the design: one to load the scan chain with the current values of the points of interest, and another to shift the contents of the scan chain by one bit. The scan chain contents are usually sent to a workstation for analysis. A model of the integrated circuit is used to match the raw data to the circuit.

Such devices have been used in CPUs since the 1980s at least, because they allow the entire state to be examined at minimal cost. It takes some time to unload a long scan chain because the information is obtained one bit at a time, so scan chains are not useful for real-time inspection of CPU internals. However, a truly synchronous circuit has the same behaviour regardless of the clocking rate. Therefore, the CPU can be "single stepped" through one clock cycle between inspections, allowing a "moving picture" of its activities to be captured, one "frame" at a time. For testing, this can be used to compare hardware against a software or HDL simulator model. JTAG provides similar test functionality, but it's not suitable for use in every environment. In an FPGA, JTAG is often used for downloading bit files, so it may not be straightforward (or even possible) to use a JTAG connection for other debugging. If it is possible to access circuits via JTAG (as in some Xilinx FPGAs), it might still be inconvenient as the JTAG lines may be connected to an FPGA programmer rather than a debugging system. Even if the JTAG lines are connected to a PC, debugging will require a special driver to communicate with them as there is no software standard for a "JTAG interface". It would clearly be better to use a widely-supported communication standard that works on any computer. Since it is based on vlabifhw, this debugging device operates via an RS232 serial connection and can also be controlled using VL2.

The debugging functionality provided by this device is also available in commercial products such as Chipscope, which provide features to make it easier to get started with debugging, such as a GUI and pre-built components for inspecting specific types of IP core such as bus controllers. Chipscope is not cheap, but time-limited evaluation versions are available from Xilinx. Some people also use the HDL simulator ModelSim to debug their designs; this has the disadvantages of low speed and an inability to test your hardware alongside hardware made by others, but you do not need to change your design to debug it.

Usage

To use the debugging device, you must install the vlabif module and add the vlabifhw hardware to your FPGA design. The debugging device hardware and drivers are included in these packages. Then:
  1. Set up a description of the circuit you wish to inspect within a "chain_config.py" file. This specifies the VHDL signals that are of interest to you. Here is an example:
    add(Read(input_name="counter_1", size=16))
    add(ReadWrite(input_name="test_in", output_name="test_out", size=8))
    add(Read(input_name="counter_2", size=20))
    add(Write(output_name="reset_counter_2", size=0))
    add(Write(output_name="step", size=20))
        
    The input_name/output_name parameters are the names of VHDL signals. If the size is greater than 0, the type of the signal is a std_logic_vector of that size. If the size is 0, the type of the signal is std_logic.
  2. Use a Python command to generate a VHDL entity that implements the debugging chain:
    import vlabif
    config = vlabif.DebugConfig(dc_name="Autogen_Debug_Entity",
                chain_config_file="chain_config.py")
    config.writeVHDL()
        
    You can choose the name of the debugging chain (dc_name) and the name of the configuration file (chain_config_file). In this case the output is "Autogen_Debug_Entity.vhd" by default.
  3. Add the debugging chain VHDL to your FPGA design, then connect it to the vlabifhw device, as shown in this example:
    use work.vlabifhw_pack.all;
    ...
    architecture rtl of test is
        signal dc_out                   : std_logic;
        signal dc_in                    : std_logic;
        signal dc_control               : DC_Control_Wires;
    ...
    begin
    ...
        -- The debugging hardware device
        vlhw : entity vlabifhw
            generic map (
                ext_channels => 1 ,
                fifo_depth => 1,
                clock_freq => clock_freq )
            port map (
                -- External hardware connections
                clk => clk,
                reset => '0',
                hw_tx => ser_txd,
                hw_rx => ser_rxd,
    
                -- Internal connections: not used in this example.
                out_channel_data => open,
                out_channel_wr => open,
                out_channel_rdy => "1",
    
                in_channel_data => x"00",
                in_channel_wr => "0",
                in_channel_rdy => open,
    
                -- Activation signal: not used
                active => open,
    
                -- Controls for device under test
                debug_clock => ...,      -- These outputs can be
                debug_reset => ...,      -- controlled from software
                breakpoint => '0',
    
                dc_control => dc_control,
                dc_out => dc_out,
                dc_in => dc_in
            );
    
        -- Automatically generated component
        dc : entity Autogen_Debug_Entity
            port map (
                counter_1 => ...,        -- Things connected here
                test_out => ...,         -- can be read/written
                test_in => ...,          -- from software. The connections
                counter_2 => ...,        -- match the chain_config.py file.
                reset_counter_2 => ...,
                step => ...,
    
                dc_control => dc_control,
                dc_in => dc_out,
                dc_out => dc_in ) ;
    
    You might want to refer to the vlabifhw hardware documentation for further instructions.
  4. Finally, write a Python program to carry out whatever debugging operations you want. To do this, you should start with one of the examples in the vlabif/vlabifhw package the download page. The examples are provided in two groups: (1) examples that require a VL2 connection, and (2) examples that use a serial port on your workstation. (The difference is just how the vlabif.VlabInterfaceProtocol() object is connected to the hardware.) A worked example for the serial port is given below, along with Python documentation for the vlabif.debug_driver module.

    Debugging Example

    The vlabif.debug_driver module contains all of the software needed to operate the debugging device and the device under test. Method calls are used to step the clock and capture/change the debug chain value. A typical usage of the vlabif.debug_driver module begins as follows:
     1  ip = vlabif.VlabInterfaceProtocol()
     2  
     3  # Connect "ip" to a serial port or to VL2, e.g.:
     4  serialport.SerialPort(ip, "/dev/ttyS0",
     5             reactor=reactor, baudrate=115200)
     6  yield ip.start()
     7  
     8  # load debugging config
     9  debug_config = vlabif.DebugConfig(dc_name="Autogen_Debug_Entity",
    10              chain_config_file="chain_config.py")
    11  
    12  # create driver
    13  dbg = vlabif.DebugDriver(debug_config, debug=False)
    14
    15  # connect debugger to vlabifhw
    16  ip.openChannel(ip.getNumChannels() - 1, dbg)
    17
    18  # do some debugging...
    19  yield dbg.reset()
    20  yield dbg.capture()
    21  data = yield dbg.downloadChain()
    22  yield dbg.clock(1)
    
    The dbg object (of type DebugDriver) has a number of methods for controlling the hardware, including: You can use getAsDict to obtain the stored debug chain value as a dictionary object. Each key is a register name. Each value is a Read or Write object, with methods including: The printDebugChain method writes the debug chain value to the standard output. This is useful as a way to quickly inspect the register values.

    A more complicated example is found within the EMS client, which uses the debug device to access the status LEDs.


    Module Documentation

     
     
    vlabif.debug_driver
    index
    vlabif.py

    # debug_driver.py
    # Virtual Lab Interface - Debug Chain Driver
    #
    # Author: Jack Whitham
    # $Id: debug_driver.py,v 1.9 2008/09/07 09:04:47 jack Exp $
    #
    #
    # Copyright (C) 2008, Jack Whitham

    # This program is free software: you can redistribute it and/or modify
    # it under the terms of the GNU General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.

    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU General Public License for more details.

    # You should have received a copy of the GNU General Public License
    # along with this program.  If not, see <http://www.gnu.org/licenses/>.
    #

     
    Classes
           
    exceptions.Exception
    SyncError
    vlabif.debug_entity.DebugEntity(vlabif.debug_entity.DebugChain)
    DebugConfig
    vlabif.vlabif.VlabGenericProtocol(processor.Twisted)
    DebugDriver

     
    class DebugConfig(vlabif.debug_entity.DebugEntity)
        Create and manage a debugging chain configuration.
     
    The configuration is loaded from a suitably-formatted
    configuration file. The writeVHDL method can be called
    to generate VHDL for the debugging chain. This object can also
    be passed to DebugDriver to carry out debugging.
     
     
    Method resolution order:
    DebugConfig
    vlabif.debug_entity.DebugEntity
    vlabif.debug_entity.DebugChain

    Methods defined here:
    __init__(self, dc_name='Autogen_Debug_Entity', chain_config_file='chain_config.py')
    Create a new configuration.
    writeVHDL(self, out_file=None)
    Emit a VHDL description of the debugging chain hardware.

    Methods inherited from vlabif.debug_entity.DebugEntity:
    getVHDL(self)
    makeDebugRegister(self, dr)

    Methods inherited from vlabif.debug_entity.DebugChain:
    finishChain(self)
    getAsDict(self)
    getAsList(self)
    getBody(self)
    getDebugChainLength(self)
    getSignals(self)
    loadDebugChainData(self, dc_value)
    saveDebugChainData(self)

     
    class DebugDriver(vlabif.vlabif.VlabGenericProtocol)
        This driver allows software to control the debugging hardware,
    usually via the Virtual Lab Interface hardware. If vlihp is a
    reference to a Virtual Lab Interface object, then DebugDriver 
    should be initialised as follows:
     
    >>> debug_config = vlabif.DebugConfig()
    >>> dbg = vlabif.DebugDriver(debug_config, debug=False)
    >>> vlihp.openChannel(CHANNEL_NUMBER, dbg)
     
     
    Method resolution order:
    DebugDriver
    vlabif.vlabif.VlabGenericProtocol
    processor.Twisted

    Methods defined here:
    __init__(self, debug_chain, less_features=False, debug=False)
    Create a new DebugDriver for the specified debug_chain,
    which should be an intance of DebugChain, such as DebugConfig.
    capture(self)
    Equivalent to setControlLines(dc_capture=True).
     
    This triggers the capture output, causing the debug chain
    to be loaded from the input registers.
     
    Returns a Deferred. The callback is called with True
    when the operation is completed.
    clock(self, n=1)
    Send one or more clock cycles to the device under test.
     
    The minimum number of clock cycles is 1. The maximum number is
    65535. The clock line is left in the zero state.
     
    Returns a Deferred. The callback is called with True when
    the operation is completed.
    downloadChain(self)
    Download the debugging chain from the
    device under test.
     
    You need to capture the debugging chain before calling this
    method, by calling capture.
     
    Returns a Deferred. The callback is called with 
    a chain_value object when the operation is completed.
    getAsDict(self)
    Get the debug chain as a dictionary object,
    where each key is a register name (a string) and each value is
    a ReadWriteRegister object.
    getAsList(self)
    Get the debug chain as a list, where each item is
    a ReadWriteRegister object.
    printChain(self)
    Print out the current value of the debug chain using
    "print getText()" for each ReadWriteRegister.
    ready(self)
    Equivalent to setControlLines(dc_ready=True).
     
    This triggers the ready output, causing the output
    registers to be loaded from the debug chain.
     
    Returns a Deferred. The callback is called with True
    when the operation is completed.
    reset(self)
    Reset the device under test, by raising the reset line,
    activating free run, stopping free run, and lowering the reset
    line.
     
    Returns a Deferred. The callback is called with True when
    the operation is completed.
    setControlLines(self, dc_capture=None, free_run_break=None, reset=None, clock=None, free_run=None, dc_ready=None)
    Set the value of the control lines.
     
    The control lines are: dc_capture, reset, clock, 
    free_run, free_run_break.
    You can call this method with zero or more of these names as
    keyword arguments. Unspecified names are not changed.
     
    The value of a control line can be True (set to 1)
    or False (set to 0) or None (don't change). 
     
    Returns a Deferred. The callback is called with True
    when the operation is completed.
    uploadChain(self)
    Send a (possibly changed) debug chain to the
    device under test.
     
    To use this function, first download the chain with
    downloadChain. Then call the setOutput method of 
    the ReadWriteRegister objects that you wish to change.
    Then, call uploadChain. Once uploadChain has completed,
    use the ready function to load the output register.
     
    Returns a Deferred. The callback is called with 
    the parameter True the operation is completed.

     
    class SyncError(exceptions.Exception)
        This exception is raised as a result of a communications
    error between the hardware debugger and the software.
     
      Methods inherited from exceptions.Exception:
    __getitem__(...)
    __init__(...)
    __str__(...)



           
      Copyright (C) Jack Whitham 1997-2011