This tutorial demonstrates VHDL bugs localization facility introduced in zamiaCAD since version 0.11.0. You can watch this lesson on youtube.
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:
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.
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;
(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:
sim = openSim() run(sim, "10000")
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"))) 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
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:
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:
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"))