Debugger

This tutorial demonstrates VHDL bugs localization facility introduced in zamiaCAD since version 0.11.0. You can watch this lesson on youtube.

Design preparation

For demonstration purposes we have taken Plasma MIPS processor design from OpenCores.org as it is of 22.06.2012 and modified its original functional test by splitting it into small diagnostic sub-tests (directory tests/). Such diagnostic set of independent tests is required for the debugging facility (involving statistical reasoning) to work.

Besides this, for readability purposes and a small speed-up, we have also added some pretty-printing code to the original testbench and made it stop at the first failing assertion met. Here is the changed code in full:

  • tbench.vhd (lines 47 and 53 to 67)
   signal FINISHED : boolean := false;
   signal PASSED: boolean;
begin  --architecture
   --Uncomment the line below to test interrupts
   interrupt <= '1' after 20 us when interrupt = '0' and not FINISHED else '0' after 445 ns;
 
   --clk   <= not clk after 50 ns; 
   
   CLOCK: process
	variable result : integer;
	variable OK: boolean;
	file F1 : TEXT open append_mode is "sim_time";
	variable my_line: line;
   begin
   	wait for 75 ns;
   	clk <= not CLK;
   	if BYTE_WE = "1111" and ADDRESS = 128 then -- the edge is about to rise  
   		result := conv_integer(data_write(7 downto 0)); 
   		ok :=  result = 49; -- 49 is ascii code '1'
   		report "finished with " & integer'image(result) & ", passed=" & boolean'image(ok) & " at " & time'image(now);
   		PASSED <= OK;
   		FINISHED <= true;
		write(my_line, "finished at " & time'image(now));
        writeline(F1, my_line);
   		wait;
   	end if; 
   end process;
   
   reset <= '0' after 500 ns;


This change is, of course, very optional and is not required by the debugger in any way, while making the VHDL code and the debugger script more readable.

Bugs introduced

  • bus_mux.vhd (lines 125 to 128)
when BRANCH_GTZ =>                                    -- should be BRANCH_GEZ
   take_branch <= not reg_source(31);
when BRANCH_GEZ =>                                    -- should be BRANCH_GTZ
   take_branch <= not reg_source(31) and not is_equal;

Debug script

(Please consider this page for the Phyton scripting support setup details.)

The following script (create_debug_data.py) is used to debug buggy Plasma design:

from java.io import PrintStream
from org.zamia.instgraph.interpreter.logger import Report

def checkSignalValue(signalName, expectedValue, sim):
  value = sim.getValue(PathName(signalName))
  return value.toString() == expectedValue

rebuild()

passedAssignments = []
passedConditions = []
failedAssignments = []
failedConditions = []

# Run Tests
for i in range(1, 45):

  if (i == 26):
    continue

  copy("tests/" + str(i) + "/code.txt", "code.txt")

  sim = openSim()
  run(sim, "10000")

  assignmentsLog = sim.collectExecutedAssignments(str(i))
  conditionsLog = sim.collectExecutedConditions(str(i))
    
  ok = checkSignalValue("RESULT", "49", sim)
  if (not ok):
    failedAssignments.append(assignmentsLog)
    failedConditions.append(conditionsLog)
  else:
    passedAssignments.append(assignmentsLog)
    passedConditions.append(conditionsLog)

reportAssignments = Report.createReport(failedAssignments, passedAssignments, "Assignments")
reportConditions = Report.createReport(failedConditions, passedConditions, "Conditions")

reportAssignments.printStat(PrintStream(file("assignments.txt")))
reportAssignments.print(PrintStream(file("assignments_full.txt")))
reportAssignments.getSuspects().printStat(PrintStream(file("assignments_red.txt")))
reportConditions.printStat(PrintStream(file("conditions.txt")))
reportConditions.print(PrintStream(file("conditions_full.txt")))
reportConditions.getSuspects().printStat(PrintStream(file("conditions_red.txt")))


The arrangement of tests into the form

tests/1/code.txt
tests/2/code.txt
tests/3/code.txt
...

is made solely for their convenient access from within the script file. Thus, the proposed script can be used with minor changes for the general case of debugging processors, whose testbench sets consist of small diagnostic tests.

Basically, the script does the following:

  • elaborates the design:
    rebuild()
  • for every test, runs simulator for 10us:
    sim = openSim()
    run(sim, "10000")
  • checks whether the test discovers a bug or not, and collects the design execution log into either passed or failed test sets:
    ok = checkSignalValue("RESULT", "49", sim)
    if (not ok):
      failedAssignments.append(assignmentsLog)
      failedConditions.append(conditionsLog)
    else:
      passedAssignments.append(assignmentsLog)
      passedConditions.append(conditionsLog)
  • finally, creates the debugger report and writes it to file:
    reportAssignments = Report.createReport(failedAssignments, passedAssignments, "Assignments")
    reportConditions = Report.createReport(failedConditions, passedConditions, "Conditions")
    
    reportAssignments.printStat(PrintStream(file("assignments.txt")))
    reportConditions.printStat(PrintStream(file("conditions.txt")))
    

One important detail to mention is the naming convention of the debugger reports. While the provided script can be run perfectly well from the command line of zamiaCAD, user has to name reports of executed assignments and conditions as reportAssignments and reportConditions, respectively, to make GUI version of zamiaCAD fetch these reports on the fly and automatically highlight suspected assignments/conditions derived from these reports.

Below is the result of running the script from the GUI version of zamiaCAD:

Suspected assignments

The script itself can be run either from the context menu of the project ("Run script...") or directly from the context menu of the script file ("Run script").

Once zamiaCAD has highlighted suspected assignments, underlying suspected conditions can be revealed by selecting the code of interest and hitting "Ctrl + Shift + C" shortcut, also available from the context menu as "Show suspected conditions" option. Here's what you should get:

Suspected conditions

Finally, the following short script (load_debug_data.py) can be used to load the output generated by the debugger from disk, thus saving time on running the debugger algorithm:

from org.zamia.instgraph.interpreter.logger import Report

reportAssignments = Report.readFromFile(file("assignments.txt"))
reportConditions = Report.readFromFile(file("conditions.txt"))

 

Theme by Danetsoft and Danang Probo Sayekti inspired by Maksimer