Patch notes
- #35 A register read before its first reset: dewfpga sim starts it where the board starts it, and the netlist keeps the state codes the student wrote · 8 October
- #34 The netlist the build writes starts every register as the bitstream does: a register without a start value at 0, a set or preset register at 1 · 7 October
- #33 The chain reads every design file, not only the ones that hold a module, and a .* connection whose port and signal widths differ is named with the fix before either reader refuses it · 7 October
- #32 A latch whose gate and data change together, and a unique casez whose items overlap: the chain names both before the board differs from the simulation · 7 October
- #31 A random-design generator checks the SystemVerilog DDCA teaches against its own simulation · 7 October
- #30 The install keeps only the chain that runs: about 0.25 GB on disk instead of 1.5 · 7 October
- #29 A failed build leaves no old bitstream, wrong pins are named, a quick save rebuilds · 4 October
- #28 dewfpga sim simulates what the build reads when iverilog refuses the design · 4 October
- #26 Optional accounts with local session handling and scoped deletion · 3 October
- #25 Mail attempts retain uncertainty and campaigns require an owner test · 3 October
- #24 Newsletter consent and working management links · 3 October
- #23 Prepare simulator discovery and enforce its browser checks · 3 October
- #22 Explain slow counters and keep Max responsive · 3 October
- #21 Board controls retain the state the student is holding · 3 October
- #20 Located diagnostics while typing · 3 October
- #19 Bring a lab folder and take it back to the CLI · 3 October
- #18 A workspace that keeps names, choices and edits · 3 October
- #17 Browser compilation checks the design and clears failed runs · 3 October
- #16 Project instructions and a measured repair experiment · 3 October
- #15 MCP tools backed by the checked CLI · 3 October
- #14 Machine-readable CLI results · 3 October
- #13 Accurate lint severity and fresh waveforms · 3 October
- #12 Vivado projects and editor tasks that preserve user files · 3 October
- #11 Five CLI steps and accurate Homebrew diagnosis · 3 October
- #10 Patch notes on the site and working navigation hit areas · 3 October
- #9 One navigation source, unchanged desktop layout · 3 October
- #8 Browser tests and checks before publishing · 3 October
- #7 Safer installation and reproducible simulator distribution · 3 October
- #6 The CLI's untested paths get checks, one number for the count, a test that reads the bitstream, the board test ready for the board · 27 September
- #5 Every message in one shape, with its code and its page · 27 September
- #4 What Vivado accepts compiles: yosys-slang as the second reader · 27 September
- #3 Silent wrongs stop the build · 26–27 September
- #2 132 SystemVerilog probes run through the product, every stage checked · 25–26 September
- #1 The CLI and the guide say the same thing · 24 September
1.2 (in progress, from 24 September 2026)
Every update below comes with the tests that prove it: what each test checks, and the numbers before and after.
#1 · The CLI and the guide say the same thing · 24 September
dewfpga uninstall says what it removes, and removes only its own. It used to print removing: ~/fpga ~/.dewfpga <link> and then delete five entries inside ~/fpga, not the folder. It also deleted Homebrew's dewfpga link even when that link pointed to another copy of the CLI. Now it lists every path it deletes, touches the link only when the link points to itself, leaves anything else in ~/fpga alone, and says nothing to remove when there is nothing. Tried on a fake install with a student project inside ~/fpga: the project file survived, the real Homebrew link survived, a second run printed nothing to remove, and an empty folder was removed.
Version 1.2.0. The CLI had said 0.1.0 since 19 September. The patch number and dewfpga --version are now the same number.
The guide stopped contradicting itself.
- Section 2 said two course-code problems "are handled for you". Only one is (the seven-segment port line); the others stop the build with the line and the fix.
- Section 7 said "one", then "two more", then listed six cases.
- Section 8 called it "the one case hit so far".
- The command list had no
--version.
Tried: writing the right count into section 7. Dropped it, because the list grows every time a new student repo turns up a new case. The text now says what each case does instead of how many there are. The HTML page and the PDF (12 pages) were rebuilt from the same source.
README.tr described rules that stopped being true on 21 September. It said the top module is chosen by its .xdc file, sim needs <top>_tb.sv, two .sv files in one folder is an error, and dewfpga check has six rows. The top now comes from the module hierarchy, the testbench is recognized by its content, file names are free, and check prints nine rows. README.tr is now a translation of the English README.
Disk space, measured.
- After the one-line install,
~/fpgaholds 1.40 GB: nextpnr-xilinx 1.06 GB, prjxray 214 MB, the chip database 89 MB, the Python venv 52 MB. - Homebrew packages add 235 MB.
- The home page said 1.76 GB, which is the by-hand path with full git clones.
- The home, why and Turkish pages now say 1.4 GB (1.76 GB by hand).
Tests. test/run.sh: 51 passed, 0 failed before the change (81 s) and 51 passed, 0 failed after. One test was renamed: "top found from xdc" is now "top found from the hierarchy", which is what it has checked since 21 September.
Not done here. The README still says "46 checks"; update #6 locks that number to the test suite.
#2 · 132 SystemVerilog probes run through the product, every stage checked · 25–26 September
In numbers. Before #2, test/run.sh passed 51 checks, and the 43 probes of the 2509 measurement lived outside the repo, run by a script that called yosys directly (33 of 43 synthesized). After: test/run.sh passes 178, fails 0, and counts 87 known gaps apart; its probe section runs 132 probes through the CLI itself. Synthesis 69/132, all four stages 30/132, and 29 of the 111 probes Vivado supports pass all four: the other 82 are our gap. 17 probes build a bitstream whose netlist fails: 15 do something else (a silent wrong), and 2 (35, an MMCM, and 97, a block RAM) have no simulation model to check them against. The product did not change in #2 (bin/ and templates/ are the same as before); #3 and #4 are scored against these numbers. Seven break rounds sent 195 reports; every one was reproduced (one of round 7's in part), seven side claims inside them did not hold (below), and what was not fixed here is listed with the update that owns it. Every probe, with its construct, Vivado's status, the source and the stage that fails, is listed under "Every probe" below.
What a student may write is now part of the test suite. test/sv/ holds 132 probes, one construct each: design.sv (one probe has design.v) with module top, tb.sv with module tb that prints PASS, sometimes a .svh, a .mem, a package, interface, submodule or netlist-form module in its own .sv, or the lab's own Basys3_Master.xdc with only some pins uncommented (96b; 93's one design file is counter.sv, as its lab has it). 43 came from the 2509 measurement (svprobe); the comments that named a student and a student's file are gone. 8 were added in the first break round, 18 in the second, 23 in the third, 8 in the fourth, 15 in the fifth, 8 in the sixth and 9 in the seventh (below), for cases the 43 did not cover. Every probe goes through four stages, all of them through bin/dewfpga and templates/Makefile, in a temp folder that holds only the probe's sources:
rtl:
dewfpga sim. Pass = exit 0 and the testbench printedPASS.synth:
dewfpga bit, with the wholeBasys3_Master.xdcuncommented as in a lab folder. Pass = the product wrotetop.json, or the netlist of another module when the CLI took that one for the top (round 4:81); the stages then check what it built, and the row's note names it.netlist: that netlist written back as Verilog (
read_json,write_verilog). Pass = both:- the same
tb.sv, simulated on the netlist with yosys'xilinx/cells_sim.v, printsPASS; - the netlist matches the RTL (
test/sv/eqv.py): both get the same inputs, every combination (all zeros included) when the design has 16 input bits or fewer, buttons included, and no clock; otherwise 4096 random cycles, each one all zeros, all ones, mostly zeros, mostly ones or even odds, with the buttons pressed one cycle in eight. Every output bit the RTL knows has to be equal; anxor azin the netlist is not equal. Both start as the board does: a register without a start value gets its primitive's default in the netlist (0, or 1 for FDSE and FDPE, as nextpnr writes them) and the same value in the RTL, and with a clock and buttons every button is held for one clock edge before the first compare, as a student presses reset after loading the board (round 4; round 3 counted such differences apart instead, which let a lost reset through); since round 7 so is every input bit that resets a register (sw[15]inif (sw[15]) s <= IDLE). The run also passes when the netlist follows, at every compare, a second copy of the RTL started asdewfpga simstarts it,xwhere it gives no start value: equal to the board's start while a register bit of that copy is stillx, and to that copy, every bit known, once none is. The board then shows what the student's own simulation shows (round 6: a register yosys moves into a ROM can start the board one clock off the usual 0; round 7 made this one decision for the whole run, not one per compare). When iverilog cannot compile the RTL (13d,18b,18c,21b,22,38,54,55,71,72,73today) this half is skipped and the result line says so.test/sv/eqv.shruns this half, andtest/sv/eqv_check.shruns the sameeqv.shon netlists that differ from their RTL in one known way (round 5; until then it kept its own copy of the RTL side).
This stage checks what synthesis made. It does not check place-and-route or the bitstream.
- the same
bit: the same
dewfpga bitexits 0 and writes the netlist's.bit(top.bit).
test/sv/expect.tsv has one row per probe: the construct, whether Vivado accepts it, the source for that, what each stage does today, and what the student sees. test/sv/run.sh fails on any difference, in both directions: a stage that stops passing, and a stage that starts passing without the table saying so. It also fails on a row without a source, on a file:line in the today column that no stage printed, and on a message the today column quotes ('...' or "...") that no stage printed word for word (round 4). It prints four numbers: synthesis, all four stages, Vivado-supported probes that pass all four, and known gaps. A known gap does what the table says, but either Vivado supports it and a stage fails, or its bitstream builds while its netlist fails (a silent wrong).
test/run.sh runs it and prints one line per probe: PASS, GAP (counted on its own, not as a pass; it does not fail the run) or FAIL. A probe that fails where Vivado refuses the code, or where what Vivado does is unverified, is a PASS, unless its bitstream builds from a netlist that fails: that silent wrong is a GAP whatever Vivado does (03, 03b, 05 and 39 are unverified and gaps; this said PASS until round 5, and so did test/run.sh's header). CI runs the same and keeps the rows as the sv-results artifact. With the measured yosys and iverilog, when #3 or #4 closes a gap the run fails until expect.tsv records the new result, so every change in these numbers is an edit of the table. With another yosys (CI installs 0.68+post) a stage that starts passing is only a note, so the table is updated from a local run.
Found on the way: the CLI's top finder misses an array of instances. inv u_inv [3:0] (...) is in UG901's list of supported Verilog, and it compiles through the CLI. What fails is the automatic top: the scanner does not read inv u_inv [3:0] ( as an instance, so inv looks like a second top, and dewfpga bit prints 2 modules could be the top (nothing instantiates them): inv, top. Name it: dewfpga bit inv, which names the wrong one. dewfpga bit top builds it (4 LUT), and so does plain dewfpga bit when the file is named top.sv. The old runner called yosys directly and never saw this, so synthesis through the product is 32/43, not the 33/43 the plan started from. The fix is #4's.
The Vivado column, checked row by row. Sources: UG901 v2023.2 (chapter, table, page), UG953 v2022.2 (the 7-series primitive guide), and Vivado logs from public CS223 lab repos. A quoted log line keeps the message; the student's module and signal names are replaced by a description. A forum is marked as a weak source. The 132: 111 supported, 3 not supported, 18 unverified (the 123 of round 6: 105, 3, 15; the 115 of round 5: 99, 3, 13; the 100 of round 4: 84, 3, 13). Changes from the 2509 measurement:
35_mmcm: unverified → supported. UG953 p.459, MMCME2_BASE: "Instantiation: Yes".37_var_init: supported → unverified. The declaration initializer is documented (UG901 p.244). Forinitial c2 = 9, UG901 contradicts itself: p.251 says "initial blocks are ignored during synthesis", p.155 initializes a RAM inside aninitialloop.33,33b(inout read-back): UG953 p.386, IOBUF "Inference: Recommended", added next to UG901 Tristates, which only covers output buffers.32(a derived clock): a Vivado 2020.1 lab log where a toggled register clocks another module, 0 errors, bitstream written.- The 2509 notes used v2022.2 chapter numbers next to v2023.2 tables; all cites are now v2023.2.
42_streaming_concat: supported → not supported (round 3): its{<<{...}}is the "Re-ordering of the generic stream: Not Supported" row (p.287), not the "Concatenation of stream_expressions" row above it.- Still unverified:
03,03b(async load),04(both clock edges),05(two drivers),37(above), and of the new ones39(an undeclared name; IEEE 1800 makes it an error),55(a conditional between two values of one enum), and from round 346(continue),48(iff),66(alias),77(aninitialthat reads a signal) and80(a statement after an async reset's if/else), from round 463(a cast of a real parameter, below), from round 637band63b, and from round 795(a module with ports and no body),98(a module namedINV) and99(a parameter of typestring). The only sources for 04 and 05 are forum posts.13b(an unpacked array port) was unverified until the second break round found UG901's own examples of it; it is supported now.
Tried, and why it changed.
- Calling yosys directly with the Makefile's script, as the old runner did. Dropped: it hid the top finder's miss on
26, and it would not measure #3 and #4, which change the product. - Uncommenting
create_clocktoo. A probe without aclkport then printscreate_clock: target [get_ports clk] matched nothing. It stays commented; timing is checked at 100 MHz, as for any lab without one. - Bash 3.2 (macOS's
/bin/bash) has nomapfileand fails on empty arrays underset -u; the runner uses neither. - The netlist of
03bnever reaches$finish; it used to show as "(no output)". It now saysTIMEOUT. - CI installs yosys 0.68+post, while
expect.tsvis measured with 0.69+post. With the measured yosys and iverilog, every difference fails. With other versions, a stage that used to pass and now fails counts, and so does a new silent wrong; a new pass is printed as a note, because students get whatever brew ships. Before pushing, all 51 were run with yosys 0.68 (YoWASP, in a scratch venv, brew untouched): the same 51 results.
Break round 1. Three independent breakers (a student who writes what Vivado accepts, a mutation tester, a regression reader) ran against #2 and sent 38 reports; some are the same break seen by two or three of them. Every one was reproduced in a scratch folder. Two side claims inside them did not hold as written: one breaker counted 20 probes hidden behind passed 94, and the table has 18 (43 minus the 25 that passed all four; 13 of them Vivado-supported, as the breaker said); another's aside, that CI's golden check would also miss every LUT INIT zeroed before nextpnr, came from reading test/run.sh, and it was not run under CI's yosys 0.68 (not verified). What changed:
- The netlist stage passed wrong netlists. With one to four vectors per testbench, 142 of the breaker's 174 single-bit LUT INIT flips in the product's own netlists passed. The netlist is now also compared with the RTL (
eqv.py, above): 204 of 228 such draws were caught (some draws were the same flip; round 2 measured distinct flips, under "Does the suite catch breakage?"). - Testbenches did not look at the construct they name.
30passed with>>>read as>>and with$signeddropped,19with a casez item deleted,18could not tell1???from10??, and17's size cast could be dropped because the output cut the value anyway.17,18,19and30now check every input combination they use against a reference written in plain integers;30shows the sign that>>>copies in (seg[6] = sh[7]), and17shifts after the cast so the cast shows. The same sweep found10's DONE state and32's hex table half unchecked; both testbenches now walk every state and every digit, and18band22(where only the testbench checks the netlist) check every combination. Source mutations over the 43 (one operator at a time, iverilog): 113 killed and 42 survived before, 122 killed and 32 survived after. The 32 left are equivalent (a 1-bit counter's+1and-1, widths the output cuts anyway, ageneratebranch that is never built, a case item that repeats the default), clock-edge swaps and reset phases a testbench cannot see,ledbits no testbench reads, and one logic change its vectors miss (36'ssw[0] & sw[1]as|). A reader fault in any of those outputs would still show in the netlist stage, whereeqv.pycompares every output bit. - Silent wrongs had no probe, and one was new.
39_undeclared_name:summforsumbuilds a 0-LUT bitstream with exit 0 (the plan's #3 lists it; the table had no row).23c_package_own_file: the CLI hands the tools only the files that hold a module, so a package in its own file is left out, yosys makescfg_pkg::MAGICan undriven implicit wire, and the bitstream builds with 0 LUT.eqv.pyfound a fifth:03's async load. WhilebtnCis held, the netlist followsswand the RTL keeps the value from the edge, so the board does not do what the simulation showed. - Constructs Vivado accepts that the 43 did not cover, now probes with their sources:
38gate primitives drivinglogic(a Vivado 2015.2 lab log builds it; iverilog 13 refuses it),16cUG901's own while-loop example (yosys refuses it),22ban interface in its own file (iverilog stops at the interface port, and the CLI leavessum_if.svout of synthesis, so yosys does not find the interface),23dan import in the module header (the CLI refuses it before any tool runs),40a module nameddisplaynext to a$displaycall (the CLI reads it as an unnamed instance),13ban unpacked array port (yosys refuses it; Vivado support found in round 2). - The runner could be fooled. It copied build outputs left in a probe folder with fresh times, so
makereused a staletop_sim; it now copies only sources. With a yosys other than the measured one (CI's 0.68), a probe that turned into a silent wrong was only a note; that now fails in every mode. A row with an empty source went through; it is now refused. The table said "the CLI names the line" and nothing checked it; afile:linein the today column now has to appear in the stage logs, so a latch error moved to line 1 fails01. test/run.shcounted known gaps as passes:passed 94included 18 probes that fail a stage as recorded, 13 of them Vivado-supported. They are nowGAPlines, counted apart.- Hygiene. The personal-path check excluded every file named
run.sh, so it never readtest/sv/run.sh; it now excludes onlytest/run.shitself.expect.tsvquoted four module and signal names from one student's Vivado logs, two of them found in only one repo of the 94; the quotes keep the log message and describe the name instead, and a check refuses a quotedmodule '<name>'again.SV_OUT=rows.tsv(a relative path) wrote into the temp folder and was deleted; it is now made absolute before the testscdanywhere. - The notes were wrong in five places, corrected above and below:
-g2005did not turn only09and10red; the netlist stage does not see the bitstream; the array-of-instances quote was paraphrased and the construct does build withdewfpga bit top; "no UNISIM models" was given as the reason for18band22; "these numbers move on their own" contradicted the runner.35's UG901 page is 232, not 231.CLAUDE.mdsaid the suite has 51 tests.
Tried, and dropped:
- Comparing netlist
xwith a known RTL bit as a mismatch: an uninitialized flip-flop in the netlist model isxwhere the RTL has a 2-stateintat 0 (10), and the board starts at 0. Anxthere was counted apart and azstill counted. Round 3 found that this let an undriven output through, and changed it (below). - Changing all inputs at once in the equivalence bench:
09's latch got its gate and data in the same instant, a race in any simulator, and showed a false mismatch. Then a random walk of one bit per cycle: no race, but29's shift register saw almost only runs of equal bits, and 4 of 12 LUT flips survived. Now every bit gets a new random value each cycle, one bit at a time: 20 of 20 in a new sample. - Decoding the bitstream back to FASM (prjxray's
bit2fasm) to check the bit stage: it needsbitread, whichinstall.shdoes not build, and a frames-to-FASM decode of30gave 195 missing and 1939 extra features next to nextpnr's FASM (default bits and pseudo-pips), so it is no quick check. A test that looks inside the bitstream is left to #6. Until then, place-and-route and bitstream writing are checked only bytest/run.sh's golden blink FASM, and exactly only with the yosys it was made with. - A probe for a design and its testbench in one file, and one for two testbenches in one folder: the runner keeps the testbench in
tb.sv, so neither fits a probe; they are left with their fixes (below).
Found and left to later updates (reproduced, not fixed here):
- #3: editing a
.memfile does not rebuild ($(TOP).jsondoes not depend on it), sodewfpga bitsays "up to date" and the board gets the old ROM. - #4: gate primitives on
logicin sim; a package or an interface in its own file (and the README and cli page sentence "Every .sv/.v in the folder is synthesized", false for them; round 4 made it say what happens); an import in the module header; a module nameddisplay; a design and its testbench in one file (sim: "already declared", bit: yosys reads the testbench); while loops; unpacked array ports; the unnamed-instance errors come out in a different order from run to run (a Python set). - #5:
04and an async reset with a synchronous clear in onealways_ffget "a signal is written from two always blocks" with no file:line. - #6: a test that looks inside the bitstream.
- #12: two testbenches in one folder;
simalways runs the first, with no way to pick the other.
Break round 2. The same three kinds of breaker ran against the tree after round 1 and sent 30 reports; two of them are the same break (13b). Each was run in a scratch folder: all 30 reproduced. Fixed here:
13b's Vivado column was wrong. UG901's own 3D RAM examples declare unpacked array ports:rams_sp_3d.sv(p.161) hasinput [A_WID-1:0] addr [NUM_RAMS-1:0]andoutput reg [D_WID-1:0] dout [NUM_RAMS-1:0], the dual-port ones (p.162, p.164) the same, and so does v2022.2.dewfpga biton UG901's file stops atrams_sp_3d.sv:10: ERROR: syntax error, unexpected '[', andtest/run.shprintedPASSfor it.13bis supported now, so a known gap.Vivado accepts 18 more things we fail, and no probe had them. Each is a probe now, with its source. (Round 3 read the sources again:
42is not supported and46,48and55are unverified, so 14 of these 18 count as Vivado-supported now.)- UG901 v2023.2 Table 27 marks these Supported, and yosys refuses each at synthesis:
41==?,42{<<{...}},43foreach,44do ... while,47a type parameter,48@(posedge clk iff en),49a default argument,50arguments by name,51an unpacked arraylocalparamset with'{...}.45breakand46continueare "Jump statements: Not Recommended", the reading16buses forreturn. iverilog refuses42,48and51too. 52an internal tristate bus, the textbook's mux from two tristate buffers. UG901 p.86: "An inferred BUFT is converted automatically to logic realized in LUTs". yosys keeps two$_TBUF_cells, nextpnr has no place for them, and the CLI says "the design does not fit the XC7A35T ... Make it smaller" for four muxes. It was then the only probe whose synthesis passes and bitstream fails; round 3 added70and75, which do too.53an async reset ORed with a synchronous clear,always_ff @(posedge clk, posedge btnC) if (btnC || btnU): a Vivado 2021.2 lab log builds it; yosys stops ("Multiple edge sensitive events").54a name used before its declaration: in the course's own example project, Vivado's simulator warns ("VRFC 10-756 ... used before its declaration") and builds;dewfpga simrefuses it,dewfpga bitbuilds it.56a file-scopetypedef enum {S0, S1, S2}:S0collides with a name in yosys' ownxilinx/cells_sim.v, and the only location printed is that yosys file. Inside the module, or namedIDLE/RUN/DONE, it builds.57a course "ready module" in the netlist form the course hands out ((* keep_hierarchy *),\<const0>, LUT primitives): Vivado 2017.3 builds the course's own example project with two of them; the CLI calls the file a leftover Vivado netlist and says to delete it. yosys given the files builds it. Of the 7 corpus lab folders the CLI refuses as netlists, 5 hold such course modules (round 5: 4 of the 5 are one repo's copy of the course's handout folder, 1 is a student's project with the modules copied in).08bthe course's port line with comments in the port list (one afterseg,, one holding a;): the port-line fix misses it and yosys stops with "Module port `\dp' is neither input nor output".55a conditional between two values of one enum (next = sw[0] ? RUN : IDLE): iverilog wants a cast, yosys builds it, slang accepts it. What Vivado does is unverified.
Every new testbench was run twice: it passes on a correct implementation (iverilog on the RTL, or, where iverilog refuses, a netlist from yosys-slang or yosys) and fails on the design with its construct broken (one edit each, 18 of 18 caught).
- UG901 v2023.2 Table 27 marks these Supported, and yosys refuses each at synthesis:
eqv.pynever compared the all-zero input. Its Gray-code walk rani = 1 .. 2^n, sosw = 0000never came up (and2^ntruncated to a repeat). A netlist wrong only atsw = 0000passed with "1048576 output bits compared". The walk starts at 0 now.eqv.pymissed faults at corner values. Random cycles drew even odds per bit, sosw = ffffcame up once in 65536 cycles. The breaker's flip in21's popcount LUT (wrong only atsw = ffff) passed 4096 cycles; a second flip, wrong atsw = 7fff, too. Each random cycle now picks all zeros, all ones, one bit in eight, seven in eight, or even odds; both flips are caught.Buttons turned the exhaustive walk off, which the notes did not say. Without a clock the walk changes one bit at a time anyway, so buttons join it (only
05has buttons and no clock).eqv.pycould be gutted with every test green (compare only bit 0, or run 16 cycles), and the round-1 "204 of 228" was a one-off measurement.test/sv/eqv_check.shnow runs six netlists that differ from their RTL in one known way: equal (has to pass), onlyled[15], only atsw = 0000, a clocked design only atsw = ffff, only atsw = 7fff, and a counter only after 200 cycles.test/run.shruns all six.test/run.shtrusted the runner's verdict and ignored its exit code. A runner that wroteokfor a failing probe, or a consumer that filedbadas a gap, left the suite green over a real regression (14's parameterized instance hidden from the top finder:PASS sv 14_params, exit 0). Now each row's verdict is worked out again fromexpect.tsvand the row's four stages and has to match; the runner's exit code has to fit its rows; its known-gap count has to equal the rows'.Runner guards that no test pinned, each now pinned by a check that breaks it on a copy:
- a testbench that prints
FAIL(dewfpga sim still exits 0) has to be an rtl fail (16cbroken on purpose); - a probe folder without a row has to be refused;
- a
dewfpga bitthat exits 0 withouttop.bithas to be a bit fail; - synth is read from
top.json, not the exit code:52pins it (synth passes, bit fails); - a silent wrong of an unverified probe (
03,03b,05,39) has to stay a gap: the re-derived verdict above pins it.
- a testbench that prints
The vivado column was not checked against its source. Changing
01tonot supportedturned its gap into a pass. The runner now refuses a row whose column its source does not back:not supportedneeds UG901's "...: Not Supported'" or invalid code,unverifiedneeds the reason no source settles it ("does not say what Vivado does", "is not described", "Weak source only", "No Vivado log", "contradicts itself"), andsupportedhas neither. Tried on 9 column-only edits (01both ways,22b,23b,05,20,18,06,13b): all 9 refused.The student-name check had no self-test, and only knew quoted
module 'x'forms. It is now a function over the source column (the today column names the probes' own modules), it also sees`\name'quotes and Vivado's(name_reg), and a check plants both and has to find them.expect.tsvsaid03and05build "with no warning". Both print yosys warnings (Async reset value ... is not constant!,Driver-driver conflict ... Resolved using constant.) and exit 0. Fixed.The notes were wrong:
22bis not refused "before any tool runs" (iverilog and yosys both run); the LUT-flip counts drew flips with replacement and the survivor list added up to 23, not 24 (now measured again, below).CLAUDE.md's corpus line said none of the 73 failing folders was the CLI's fault; 17 are Vivado-accepted code we refuse, and 5 of the netlist folders are course modules.
Tried, and dropped:
- Checking new testbenches on slang netlists only: slang refuses
44("unsupported language feature"),53("condition cannot be matched to any signal from the event list"),54(it enforces declaration before use) and57(no cell library), and its netlist of52leaves the busx. Those were checked on iverilog or yosys instead. - Re-deriving the verdict in
test/run.shalone: the runner's own verdict was then still unpinned (a runner that writesokforbadwent unnoticed, because the consumer caught the probe anyway). The row check now also needs the runner's row to saybad. A separate branch for "stages differ" turned out to duplicate the verdict comparison (breaking it changed nothing); the two are one branch now. - A keyword check for
supportedrows: their sources share no phrase (Table 27 rows, coding examples, attribute pages, logs), so only the two directions that hide a gap are checked. - A probe for a module defined in two files, and for a rebuild after a file is deleted or an included
.vchanges: the runner copies each probe into a fresh folder, so no probe can see a rebuild. They go to #3 with their fix and its test. - A probe for CRLF line endings: no stage looks at line endings. Left to #4 with the port-line fix.
Found and left to later updates (reproduced, not fixed here):
- #3: a module defined in two files: the scanner keeps the last file that defines it, so a backup (
lab4_old.sv) is built instead oflab4.sv(0 LUT, exit 0), anddewfpga simcompiles the backup; 3 of the 89 corpus lab folders define a module in both a.svand a.vfile (a fourth does it in a Vivado netlist, which the CLI already refuses).dewfpga simandbitdo not rebuild when a file is deleted or swapped (a deleted testbench keeps running) or when anincluded.vchanges (bitsays "up to date"): the same class as round 1's.mem. - #4: the 18 new constructs,
13b, and in sim54and55; the port-line fix with comments (08b);--fix-portsrewrites every CRLF line ending of the file it fixes (15 of the 364 corpus.sv/.vfiles are CRLF); the course's netlist-form modules refused as leftovers (57). - #5:
52's "does not fit ... Make it smaller" for$_TBUF_cells;53's "two always blocks" advice for one always block. The guide's section 7 says the async-load reset and the dual-edge cases "both stop with the line and the fix" (03builds with warnings,04has no file:line), quotes a netlist message ("take the original .sv") the CLI does not print, and section 8 says section 7 "lists the cases hit so far" (it lists 5; the probes record 37 Vivado-supported failures). #3 turns03into an error; the catalog is #5's. - #6: the pnr line prints a garbled clock name when the net's name has a
:in it (03:416: emulate_split_set_clr$1652.C': 338.98 MHz).
Break round 3. The same three kinds of breaker ran against the tree after round 2 and sent 25 reports. Each was run in a scratch folder: all 25 reproduced. Fixed here:
- Vivado accepts 20 more things, and no probe had them. Each is a probe now (
58to80), with its source. Every new testbench passes on a correct design (iverilog on the RTL, or, where iverilog refuses it, a hand-written equivalent) and fails on the design with its construct broken (28 edits, 28 caught).- Two new silent wrongs.
58a hierarchical name,assign led[1:0] = u_fsm.state;(UG901 Table 20 p.239, Table 27 p.288: Supported): yosys makesu_fsm.statea 1-bit implicit wire with no driver, soled[1]is a constant 0 andled[0]is undriven, and the bitstream builds (exit 0) whiledewfpga simshows the state. (This said "with the state LEDs dark" until round 4; what an undriven LED shows was not measured.)59$isunknown(sw)(Table 21 p.240): yosys makes it the constant 1, so the LED is lit for every input. - UG901 rows marked Supported that yosys refuses at synthesis:
60pass by reference,61enum ranges ({S[3]},{T[5:7]}),62the type operator,63int'(STEP)of a real parameter,64an unpacked struct,65a streaming concatenation as the target ({>>{a, b}} = sw[7:0]),67==on two unpacked arrays,68an unpacked array slice,69disableof a named block. iverilog refuses60,62,64,65,67and68too. 70aworand awandnet: the netlist is right, but nextpnr has no place for yosys'$andand$orcells, and the CLI says the design "does not fit the XC7A35T ... Make it smaller", the same false diagnosis as52.71a whole unpacked array assigned inalways_ff,72a void function with output arguments,73an unpacked array as a function argument: yosys builds them right, iverilog 13 refuses them, sodewfpga simfails.74a.vfile that usesbitandfinalas names. UG901 p.269: Vivado compiles*.vas Verilog 2005 and*.svas SystemVerilog. The CLI reads every file as SystemVerilog, so both sim and bit stop. 0 of the 13 corpus.vfiles use such a name.75a design that misses 100 MHz (a 14-bit value turned into four registered decimal digits: 33.61 MHz). A Vivado 2015.2 lab log in the corpus routes at WNS -20.758 ns, prints a critical warning ("The design failed to meet the timing requirements") and writes the bitstream. The CLI stops with "timing not met ... no bitstream written", which is the recorded decision inCLAUDE.md("timing FAIL = hata"). The probe records the difference; which rule wins is the owner's call.76implicit nets the standard allows, from a port connection and from the left of anassign. They build today, with warnings, and a Vivado 2021.2 log of a public CS-224 repo builds one. #3 plans-noautowirefor39; with it,22,22b,22c,22d,22f,23c,39,58and76stop at synthesis (round 6: a sweep of all 123 probes, each read with and without it; this list named 6 of them until then). For22c,22d,22f,39and58that is the change wanted, a silent wrong becomes an error;23cand22bneed #4's file fix anyway;76(legal implicit nets) and22(plain modports, which build right today) are there to keep legal code building.78UG901's own(* keep_hierarchy = "yes" *)(p.60): the CLI's netlist detector matches that text and tells the student to delete their design.79the same attribute without spaces builds.- Unverified in Vivado, patterns from the corpus:
66alias(UG901 p.286 lists both "Aliases: Supported" and "Net aliasing: Not Supported"),77initial num = sw;(a Lab 5 file; yosys stops with no file:line),80a statement after the reset's if/else in analways_ffwith an asynchronous reset (a Lab 5 folder).
- Two new silent wrongs.
- The Makefile's async-reset rule is live. The plan calls it dead code on yosys 0.69 and #5 is set to delete it.
80triggers it on yosys 0.69+post ("Async reset \btnC yields non-constant value", then the rule's advice), and so does one corpus lab folder. Its advice does not fit that case (the reset branch assigns onlyq); that text is #5's. - The equivalence bench let an undriven output through. It counted an
xin the netlist apart, to excuse flip-flops without a start value, and yosys writes an undriven output asOBUFwith.I(1'hx). The breaker's netlist withled[4]undriven printedEQV PASS: ... (65536 more were x in the netlist only). Now:eqv.py --netliststarts the netlist as the board does: an INIT that is x gets what nextpnr-xilinx writes (itsxilinx/fasm.cc: FDRE, FDCE, LDCE and LDPE start at 0, FDSE and FDPE at 1, and an x bit of any other INIT is 0; this said LDPE at 1 until round 4, which the code does not say);- an
xor azin the netlist where the RTL knows the value is a mismatch; - the RTL starts a register without a start value at
x, and acaseon anxtakes its default branch:32's digit select gavean = 0111in the RTL and1110on the board until the reset. A third copy of the netlist, with its INITs left x, told these apart: a difference where that copy isxwas counted on its own. Only32had any (2 bits). Round 4 found that this excused every difference of a netlist that never resets, and replaced it. - The breaker's netlist now gives
EQV MISMATCH ... led[4] rtl 0, netlist x.
- A submodule yosys kept broke the bench. The netlist's
module inv(kept forkeep_hierarchy) clashed with the RTL'sinv.eqv.py --netlistrenames every design module of the netlist and the cells that instantiate them.79pins it:BAD ... 'inv' has already been declared in this scopeon the round-2 tree,oknow. eqv_check.shdid not pin what the notes promise. Removing the exhaustive walk, the mostly-zeros values or the button presses, or counting azas unknown, left all six cases green. It now has 15 cases (new:walka fault only atsw=1234,sparseonly atsw=0100,buttononly whilebtnCis pressed,zout,xout,init,xstart,hier,hierfault), and every netlist goes througheqv.py --netlistas the product's does. 16 mutations ofeqv.pyeach turn at least one case red (list below).- In CI's mode the netlist stage could be emptied. With a yosys other than the measured one, a stage that starts passing is only a note, so deleting the netlist-vs-RTL comparison left the suite green (
03became a note). Two runner checks now run in that mode, each on a design whose netlist differs from its RTL (`ifdef SYNTHESIS, which yosys defines and iverilog does not):07with a testbench that sees nothing, so only the comparison can catch it, and54, whose RTL iverilog cannot compile, so only the testbench'sFAILon the netlist can. Deleting the comparison turns the first red; dropping the check for the testbench'sPASSturns the second red. - CI's step summary got 8 score lines. The runner checks' broken copies wrote theirs too. They now run without
GITHUB_STEP_SUMMARYandSV_OUT, and a check fails if one of them writes a summary (FAILon the round-2 tree,PASSnow). The final run writes one line. 08's port-line note could name the wrong line with every test green: its today column was-. It now quotesdesign.sv:2, so the note moved to line 3 fails08(expect.tsv says design.sv:2, no stage printed it).55's testbench missed 23 of the 36 LUT flips that change what its netlist does (every one of its 48 LUT bits flipped; "changes" = the netlist stage's bench sees a difference against the unflipped netlist). It now goes through every reachable state with every value ofsw[2:0]andbtnC: 36 of 36. It also catches one flip that does not change the board, only keeps the simulation'sxstart state from clearing.- The Vivado column, read again.
42is not supported:{<<{...}}is "Re-ordering of the generic stream: Not Supported" (p.287); the "Concatenation of stream_expressions: Supported" row above it is the stream without re-ordering.46continueis unverified: "Jump statements: Not Recommended" is all the guide says.48iffis unverified: the same page lists "Iff event qualifier: Supported" and "Conditional event controls: Not Supported", and Conditional event controls (IEEE 1800-2017 9.4.2.3) is where the standard definesiff: slang's table of LRM clauses names 9.4.2.3 so, and its test for it is the standard'salways @(a iff enable == 1).45stays supported and now cites p.280 ("use it with the break statement"). The column check now lets a source that shows the guide contradicting itself be unverified even when it quotes a Not Supported row (48,66); 9 column edits of the changed and new rows were tried, all 9 refused. 57's log quote came from the wrong run. Its synthesis log holds two runs, and the quote was from the first, where the keypad module was still RTL. It now quotes the second (the LUT primitives pulled in fromunisim_comp.v,(11#1), 0 warnings). Which run the implementation used, the logs do not say.CLAUDE.md's corpus line added up to 78 of 73. It now gives the measurement saved on 24 September (corpus_results.json, 89 folders): 8 build, 40 are testbench-only, 23 need their own pin file, 7 hold netlist files, 10 hold code Vivado accepts or that is unverified, 1 is invalid code. (Round 7: of the 8,dewfpga bitalone built 2; 6 built only with a top the corpus harness named after the CLI refused to pick one, and 2 of the 8 are not the module the student's Vivado project built.) (Round 3 wrote 24 and 9, and called one folder "two clock edges"; round 4 counted again, below.)
Tried, and dropped:
- Counting an
xin the netlist as a mismatch, with only the board's start values:32failed on the RTL'scaseover an unset register. Hence the third copy. - In
eqv_check.sh, readingcells_sim.vwithread_verilog -lib: yosys keeps its flip-flops as whiteboxes, which JSON cannot hold (-nowbdrops them). Then a register's start value was lost inwrite_verilog(it stays an attribute of a merged wire) untilopt_clean -purge. - A probe for a concurrent assertion in the testbench (
assert property): iverilog 13 refuses it, and the runner runs the same testbench on the netlist, so the probe would read as a silent wrong. - A probe for a testbench without
$finish: the runner needs it to end.
Found and left to later updates (reproduced, not fixed here):
- #3: the silent wrongs
58and59;-noautowirehas to keep76building. - #4:
60to65,67to69, and71to73in sim;74, reading.vfiles as Verilog 2005 (the CLI check ".v file with SystemVerilog inside builds" says the opposite of Vivado's default);78, with57's netlist detector;70's cells;75, where Vivado writes the bitstream and the recorded decision says timing FAIL is an error (the owner decides); a concurrent assertion in a testbench (dewfpga simstops with "Try -gno-assertions or -gsupported-assertions", flags the CLI cannot pass). - #5:
70's "does not fit" for two gates;77's "Failed to get a constant init value for \num: \sw" with no file:line and no fix; a module whose file is missing from the folder ("Module\adder4' referenced in module\top' ... is not part of the design", no file:line;dewfpga simdoes printtop.sv:2); the async-reset rule's advice for80; the plan's "dead rule" line. - #6: a Vivado-style testbench (a free-running clock, no
$finish) waits 120 s and fails, and its advice, "SIM_TIMEOUT=600 dewfpga sim waits longer", cannot help it (measured: 120.48 s, exit 2). Vivado's simulator stops at its run time: the run scripts Vivado writes for each simulation (.tcl) in the corpus sayrun 1000ns31 times andrun 20000nsonce; the.logfiles holdrun 1000ns17 times in 10 files andrun 20000ns4 times in one (round 5: this said "the simulation logs" for the script counts). 10 portless module definitions in the corpus have a free-running clock and no$finishor$stop(round 5: this said 9; counted as a module with no ports or an empty port list,always #orforeverin it, and no$finishor$stop; how the 9 was counted is not recorded).
Break round 4. The same three kinds of breaker ran against the tree after round 3 and sent 23 reports; two of them are the same break (a netlist that lost its reset), so 22 breaks. Each was run in a scratch folder: all 22 reproduced. (This said "sent 22 reports" until the final count.) A first attempt at this round stopped halfway (a network outage); its eqv.py rewrite and eight probe drafts were checked here, one draft was rewritten (81) and the rest were finished. Fixed here:
- Vivado supports 8 more things that no probe had. Each is a probe now, with its UG901 row. Every new testbench passes on a correct design (iverilog on the RTL; for
13c,18c,21b,22eand64b, which iverilog refuses, a yosys-slang netlist) and fails on the design with its construct broken (9 edits, 9 caught).- Three new silent wrongs.
22can interface with ports (Table 27 p.288, 'Ports in interfaces: Supported'): yosys makesc.qan implicit wire, and the bitstream builds with 0 LUT and 0 FF, exit 0, whiledewfpga simshows the counter.22dan array of interfaces ('Array of interface: Supported'):p[0].dandp[1].dbecome 1-bit implicit wires, soled[1]showssw[0]andled[0]sw[4].81a design module that calls$finishin a simulation check (Table 21 p.239, '$finish: Ignored'): the CLI's scanner takes a module that calls$finishfor a testbench, sodewfpga bitbuilds its submodulecounteras the whole design (counter.bit, exit 0, no warning) anddewfpga simexits 0 printing nothing. The breakers' third form, a modport-typed port on an interface with aclkport, is the same class as22c(0 LUT, 0 FF, exit 0;simstops at the port) and is not a probe of its own. - Refused:
13can unpacked array concatenation,22ea modport expression,64ban unpacked union (sim and bit);18cunique0 ifand21ban assignment inside an expression (sim only: yosys builds them right). 81needed a runner change. The runner read onlytop.jsonandtop.bit, so81read as "synth fail, bit fail", a refusal, while the student gets exit 0 andcounter.bit. The runner now checks whatever netlist the CLI wrote and the row's note names the module; a runner that reads onlytop.jsonturns81BAD.81's first draft held both modules indesign.sv. The CLI then hands yosys that file,$finishincluded, and yosys stops ("System task `$finish' outside initial block is unsupported."), an error rather than the silent wrong the breaker found. The submodule is in its own file now, as in the breaker's repro.
- Three new silent wrongs.
- The equivalence bench passed a netlist that lost its reset. Round 3 excused a difference while the netlist's copy with INITs left x was x; a netlist that never resets keeps that copy x forever. On the product's own netlists with every flip-flop reset or set pin that is not a constant tied to 0, round 3's
eqv.pyprinted EQV PASS for all six probes that have such pins (09,10,21,31,32,34; 31956, 20140, 96886, 96814, 46705 and 24440 bits excused). The excuse is gone. The RTL's registers that are x get the board's start value (from the RTL read by yosys up toproc: 1 where a reset sets the bit to 1, which yosys maps to FDSE or FDPE, else 0), and with a clock and buttons the bench holds every button for one clock edge before the first compare, so a state machine yosys re-encoded one-hot, which starts in no state on the board, is compared after its reset. All six: EQV MISMATCH now. The same change catches a start value the netlist lost (lostinit).- Setting a start value bit by bit broke
07:statetype [1:0] stateis a packed array of enums, and iverilog readsstate[2]as an enum, not a bit ("Index state[2] is out of range", so EQV FAIL). A register is now read whole, its x bits set and written back through a concatenation; a variable of one enum type keeps bit selects (it takes no vector without a cast), and a 1-bit enum gets its item by name.
- Setting a start value bit by bit broke
eqv.pystarted an LDPE at 1, and the notes said nextpnr does. nextpnr-xilinx'sfasm.cc(commit 3fd7878):int def_init = (type == "FDSE" || type == "FDSE_1" || type == "FDPE" || type == "FDPE_1") ? 1 : 0;. The product's FASM for a latch with a preset holdsAFF.ZINI: it starts at 0.eqv.pygives an LDPE 0 now; round 3's gave 1. No probe has an LDPE, so no verdict changed.- Ports named
xormodebroke the bench ('x' has already been declared in this scope): the bench declared a reg per input under the port's own name next to its ownx,mode,seed. Every name it declares has a prefix now. - Parts of
eqv.pyno test pinned, each pinned now by aneqv_check.shcase: the buttons in the exhaustive walk (cbutton), theinoutpins driven from outside and compared (ioequal,ioread,iofloat), all 4096 random cycles (deepnow needs 4000; 512 and 3999 turn it red), the reset press before the first compare (recodeshows its IDLE state, so a netlist in no state differs at once), the start values (fdse,ldpe,ldfill,fillset,fillaset,pkenum,enum1), and the lost reset and start value (lostreset,lostinit).eqv_check.shhas 30 cases;test/run.shruns all 30. (Round 5: those cases pin the RTL start values of flip-flops with a synchronous reset only. Not starting async-reset flip-flops or latches, or reading a reset value's bits in the other order, left all 30 green; round 5 added the cases.) - The score line was unchecked.
test/run.shcompared only the runner's known-gap count. It now counts the whole line again from the rows andexpect.tsv(synthesis, all four stages, supported and all four, known gaps). A runner that counts every supported probe as passing (the breaker's 77/77) or counts synthesis from the bit stage: green on round 3's tree, FAIL now. - The today column's quotes were unchecked. Only its
file:linewas. The runner now also looks for every message the column quotes, word for word (a quote may leave text out with...).01quotes the latch error and its advice,02the fix. The breaker's mutation, the latch advice replaced with "something went wrong.", was a gap on round 3's runner and is BAD now. Rows that quoted what no stage prints were reworded:08band47(code, not messages),75(the recorded decision, not a log),81;22e,38and71quote messages that hold apostrophes in double quotes. The check fails with any yosys, as thefile:linecheck does. Under CI's yosys 0.68 (the YoWASP shim of round 3, brew untouched) all 100 probes matched except75, whose quote held the frequency nextpnr reached (33.61 MHz from 0.69+post's netlist, 33.91 MHz from 0.68's); the number is outside the quote now. - Checks no test pinned: the
.vin the runner'sfile:linepattern (a new check makes the CLI skip.vfiles, and74has to sayexpect.tsv says design.v:3, no stage printed it), and the student-name check's`\name'form (its self-test now plants that form too). - Rows.
22's source cited 'Modport expressions', the.name(expression)form that is22enow;22uses plain modports, which UG901 shows in its 'Modports' section (p.283-284).58saidled[1:0]undriven:led[1]is a constant 0,led[0]has no driver, and what the LED shows was not measured.63was supported with "the probe applies no operator to a real", but UG901 names the cast 'Cast operator' and lists 'Operators with real operands: Not Supported' next to 'Real ... data types: Supported' and "They can only be used as parameter values": the guide contradicts itself, so63is unverified. The column check did not see63's paraphrase; it guards the two directions that hide a gap, and a supported row that names a Not Supported row errs toward counting one (16band18name one to say why they are supported). - Sentences on the site and in the guide that #2 shows false. Guide section 7 said one course file breaks and it is fixed for you:
57(the course's netlist-form ready modules) is refused and08b(the port line with a comment in it) is not fixed. It also said the async-load reset and the dual-edge case both stop "with the line and the fix" (03builds with warnings,04has no line), quoted "take the original .sv" (the CLI says to delete the file), listedinterfacewithmodportas something Icarus accepts (only while no module port is typed with it:22, measured), and said a BOM went through (the CLI refuses it, with the command that removes it). Section 8 opened with "This covers the part of Vivado that CS223 uses, and no more" and named only the IP Catalog; it now names the language, with the 25 September numbers and a link toexpect.tsv. The error pageport-neither-input-nor-outputcalled the seven-segment module "the one course file that breaks" and said the language does not readdpas an output (the guide and08's source say it does). The why page called the IP Catalog "the real limit"; it and the home page (English and Turkish) now name the language gap too. The README, its Turkish copy and the cli page said every.sv/.vis synthesized and a module with$finishis the testbench, "as in Vivado": they now say that a package or an interface alone in its file is left out, and that a design module calling$finishis taken for a testbench for now (22b,23c,81). The guide's HTML and PDF were rebuilt (13 pages;CLAUDE.mdsaid 12). The desktop design is untouched: text only. - The notes and
CLAUDE.mdwere wrong, corrected above: nextpnr starts an LDPE at 0; 25 of the 37 netlist-passing probes have LUTs, not 22;58is missing from the-noautowirelist and its LEDs were never measured "dark"; round 2's "the only probe whose synthesis passes and bitstream fails" and "18 more things we fail" were overturned in round 3; the corpus has 23 pin-file folders and 5 unnamed-instance folders (10 in that group), and its "two clock edges" folder fails onif(reset || clear)in analways_ff @(posedge clk, posedge reset), probe53's pattern.
Tried, and dropped:
- Setting a register's start value with
forceandrelease: iverilog wants the same cast there ("This assignment requires an explicit cast.") for an enum. Walking a 1-bit enum to its item with.first()and.next()through a hierarchical name: vvp stops at an assertion inv2009_enum.c. - Refusing any "not supported" in a supported row's source:
16band18say it to explain their column.
Found and left to later updates (reproduced, not fixed here):
- #3: the silent wrongs
22c,22dand81become errors (81: a design module is taken for a testbench because it calls$finish). - #4:
22cand22dcompile (interface ports, interface arrays),22e,13c,64b, and in sim18cand21b;81builds as Vivado builds it, which needs the scanner and yosys (it stops at$finishin an always block) to ignore$finish; the course's netlist-form modules (57) and the port-line fix with comments (08b), both now in the guide as open.
Break round 5. The same three kinds of breaker ran against the tree after round 4 and sent 22 reports. Each was run in a scratch folder: all 22 reproduced. Two side claims inside them did not: that a course project hits the same eqv.py bug through a wire named message (grep -rlE 'message' over the corpus .sv and .v files, also without case: 0 files; the Lab 4 shape the fix covers was found in one repo's logs and is the case regport), and that the Vivado 2021.2 lab writes its range-only port in an always_ff (it is an always_comb, as 08c's source says). A first attempt at this round stopped before it changed anything (a network outage). The eight probe folders a round-4 attempt had left without rows (13c, 18c, 21b, 22c, 22d, 22e, 64b, 81) already had their rows when this round started: 100 folders, 100 rows, and the runner's folder check agreed. Fixed here:
- The netlist stage's RTL side had a copy that no test ran.
eqv_check.shlisted the RTL's registers with its own copy of the runner's yosys command, so deletingflattenfrom the runner left every test green while its comparison passed a netlist whose clock divider, in a submodule, counts the wrong way (EQV PASS: 147456 output bits compared, all equal: the submodule's register was never started, so its LEDs stayedxin the RTL and were never compared). The comparison is one script now,test/sv/eqv.sh: the runner calls it on the product's netlist, andeqv_check.shcalls the same script on its netlists, which now arrive as the product's do (JSON, the cell library as blackboxes). New casessubregandsubfault: a counter with no reset and no start value in its own module; the netlist counts down insubfault, which has to fail. Deletingflattenfromeqv.shturnssubfaultred, and deletingprocturns 25 cases red.eqv.shalso printsEQV FAILwhen the bench does not compile or compares nothing, so a FAIL case now needs the bench's own count of differing bits; neither of those passes one. eqv.pystarted a submodule's input port instead of the register behind it. A registernoftopthat feeds a submodule's portD, next to a register oftopalso namedD(the shape of a CS223 Lab 4 two-bit counter in the corpus): afterflattenthe bit is callednandA1.D, both names end in a name the always block writes, andA1.Dsorted first. The bench then set a net and did not compile ('eqv_rtl.A1.D' is not a valid l-value for a procedural assignment.), so a correct netlist read asnetlist fail.rtl_startsnow looks first at the names declared in the module the always block is in (each instance's module lines come from yosys'$scopeinfocells). The same fix covers a top wire named like the submodule's register it carries (subreg'sq). New caseregport, the breaker's design: red before,EQV PASS: 196608 output bits compared, all equalafter.- Parts of
eqv.pyno case pinned. Each of these left all 30 cases green and now turns one red: the compare after the buttons change, before the clock edge (mealyb:led[0] = q & btnC, lost in the netlist;EQV FAIL: 198 of 262144); starting async-reset flip-flops (xstarta, an FDCE), latches (xstartl, an LDCE) and each bit of a reset value in its own order (fillasym, a reset to4'b0011: FDSE for bits 0 and 1, FDRE for 2 and 3). Round 4's notes said the start values were pinned; only a synchronous reset's were. - Runner guards no check pinned. The check of
"..."quotes in the today column (02quotes the unnamed-instance fix that way): deleting it left the suite green, and with the fix text gone from the CLI,02still matched. The value check of the vivado column: deleting it let a row writtenSupportedthrough, and01stopped counting as a gap. Both are pinned now (st_fixtext,st_vivvalue), and the CLI checkunnamed instance -> line and fixnow looks for the fix too. - Vivado accepts 15 more things that no probe had. The breakers found 14;
51cturned up while one of their notes was measured. Each is a probe now, with its source: 13 fail a stage,51cis a new silent wrong among them, and84passes all four. Every new testbench passes on a correct design (iverilog on the RTL; where iverilog refuses it, a yosys-slang netlist, and for08cthe linebitrewrites) and fails on the design with its construct broken (18 edits, 18 caught).08cthe course's port-line shape where a port has only a range (output logic [6:0] seg, [3:0] an), written in analwaysblock. A Vivado 2021.2 lab in the corpus builds it (0 errors, bitstream written; its port list isoutput logic [2:0] <one>, [2:0] <other>and the second is written in analways_comb, not thealways_ffthe breaker named).dewfpga simstops ('an' is not a valid l-value),dewfpga bitwrites the direction in and builds, and after thatsimpasses: the same folder passes or failssimby which command ran first. A strict reader calls that port a net (IEEE 1800-2017 23.2.2.3, slang: "cannot assign to a net within a procedural context"); Vivado and the product's port-line fix are the lenient ones.13da packed 2-D array indexed by a variable, then a bit or part select (grid[i][j] <=in a loop,grid[sw[13:12]][3:1]): iverilog refuses both, yosys builds it right. A Vivado 2019.1 course project in the corpus reads and writes alogic [7:0][7:0]array with two loop indices,[i][j], and builds.12ca packed array of structs indexed by a signal, then a member select (cars[sw[15]].x): yosys stops with "Index in generate block prefix syntax is not constant!", about a generate block the code does not have.33cthe textbook's tristate buffer module (assign y = en ? a : 4'bz) in a submodule, four instances each driving a nibble of an output port: nextpnr has no place for yosys'$_TBUF_cells and the CLI says the design does not fit. One instance driving the whole port, or the same buffers intop, builds (measured). The testbench wantszon a disabled nibble, so a fix that turns these buffers into logic (as52's internal bus should be) would fail it.51ba packed 2-Dlocalparamas a seven-segment table: both tools refuse it.51cthe same table with its type named first,typedef logic [3:0][6:0] tab_t; localparam tab_t SEG = .... The breakers reported that this form builds; it does, as zeros:pnr ok: 0 LUT, 0 FF,segis 0 for every input in the netlist (every segment on), exit 0 and no warning. yosys-slang's netlist of the same file gives the right table. iverilog 13 stops at an assertion. A new silent wrong.40ba module name and(inside a$displaystring in a design module ("counter (u_cnt) wrapped"): the scanner reads comments out but not strings, so it reports an unnamed instance on the$displayline and suggests an edit inside the string.40covered only$display(itself.82$fopen,$fdisplayand$fwriteleft in a design (UG901 Table 21: Ignored) and83$realtobitsand$bitstoreal(Supported): yosys cannot resolve them. iverilog refuses83too (it cannot evaluate$realtobitsin a parameter).- From UG901 Table 27, rows the breakers checked by hand:
85a bitstream cast to an unpacked array,23ea package that exports a name it imported,86extern module,87aconfig(Table 20 too),88a class's static function. All five stop at synthesis; iverilog simulates23eand87(it skips theconfigwith a "sorry"). 84arepeatwith a constant count inalways_comb(UG901 Table 20: Supported): it passes all four stages. It is a probe for iverilog's warning, whichdewfpga simprints: "A repeat statement cannot be synthesized in an always_comb process." The row quotes it, so when #5 filters the warning the row has to change.
- Sentences #2 wrote or left false.
- The home, why and Turkish pages said each construct is measured, and the guide said
test/svholds one probe per construct a student may write. This round found 15 more; they now say "the constructs measured so far", and section 8 says the list is not every construct. Its numbers are the 26 September ones: 73 of the 99 Vivado-supported constructs fail a stage (52 stop before a bitstream, 12 fail only insim, 1 has no model, 8 build a wrong bitstream). - Guide section 2 said every other Vivado-accepted case "stops with the line and the fix. Section 7 lists them."
04,53,77and85stop with nofile:line,56with a line in yosys' owncells_sim.v,16cwith no fix,52,70and33cwith "does not fit",12cwith a message about a generate block, and the silent wrongs build. It now says so. Section 7 now lists the two corpus patterns it left out (53's async reset ORed with a clear, one folder, and80's statement after the reset'sif/else, one folder), and says that onlybitwrites a port's direction in (08c). Its "5 of 89 lab folders from old student repos" now says that 4 of the 5 are one repo's copy of the course's handout folder. Section 2 also said every.svis synthesized and named only$finish: it now says what the README says. - The README, its Turkish copy and the cli page told the student to keep
$finishin the testbench; a design module that calls$stopis taken for a testbench the same way (the scanner matches$finish|$stop, measured with81's files). They now name both. - These notes: a failing unverified probe was called a
PASS, but03,03b,05and39build a bitstream from a failing netlist and are gaps (the notes andtest/run.sh's header, both fixed); round 3'srun 1000nscounts were from Vivado's.tclrun scripts, not its logs; the portless free-running modules are 10, not 9; round 4's start values were pinned for synchronous resets only; round 2's five netlist-form folders are four copies of one handout folder and one student project.CLAUDE.md's corpus line says the same now. - The guide's HTML and PDF were rebuilt (13 pages). The desktop design is untouched: text only.
- The home, why and Turkish pages said each construct is measured, and the guide said
Tried, and dropped:
23easimport p1::*; export p1::*;: yosys-slang saysKis undeclared, and it is right. IEEE 1800-2017 26.6 exports only names actually imported, and a wildcard import makesKa candidate until the exporting package uses it. The probe imports and exportsp1::Kby name; iverilog then simulates it (with the wildcard it could not bindKeither).regport's first netlist was written as RTL (always_ff): its flip-flops then startxin the netlist, and the bench, rightly, called that a difference. The case builds its netlist from FDRE primitives, as the product's is.- A first sweep of the
eqv.pymutations printed "red: none" for every one: zsh does not split an unquoted list, so each run checked only theequalcase. The sweep now splits the list in bash; the results below are from that run. - A probe for the XDC forms
[get_ports {sw[*]}]and[get_ports {sw[0] sw[1]}](break 9): every probe uses the course's master XDC, so the form cannot be a probe without a second runner path. It is left with its fix (below). A$stoptwin of81: the scanner matches both with one pattern, so the text fix above is what was false; no probe.
Found and left to later updates (reproduced, not fixed here):
- #3: the silent wrong
51c. - #4:
08cand13din sim;12c,23e,33c,40b(the scanner has to skip strings, not only$display(),51b,82,83,85,86,87,88; the XDC forms Vivado accepts inget_ports, a wildcard (sw[*]) and a list ({sw[0] sw[1]}): the check reads only the first name and stops with "PACKAGE_PIN but no IOSTANDARD" for every port, and nextpnr-xilinx refuses both forms too (#6 owns the IOSTANDARD test path). - #5:
12c's message about a generate block,33c's "does not fit",85's message with nofile:line,40b's fix that edits a string, and iverilog'srepeatwarning that84quotes.
Break round 6. The same three kinds of breaker ran against the tree after round 5 and sent 36 reports. Each was run in a scratch folder: all 36 reproduced (a mutation report by its mutation and the checks that could catch it, not by a full suite run each; one had a moved line: the lenient-verdict mutation is on test/run.sh line 130, not 126). A first attempt at this round stopped before it changed anything (a network outage). The eight probe folders the task named as lacking rows (13c, 18c, 21b, 22c, 22d, 22e, 64b, 81) have had their rows since round 4: 115 folders, 115 rows. Fixed here:
The netlist stage failed correct netlists of three shapes.
- A wire that names a slice of an output register (
wire [3:0] low; assign low = led[3:0];; a corpus dot-matrix project has the same shape):rtl_startspreferred a name that is not a port, pickedlow, and the bench did not compile ('eqv_rtl.low' is not a valid l-value). It now looks for the name the always block writes whenever more than one name is left. That lookup read whole source lines, so anassignon the always block's line counted as written too; it now cuts the text at the columns of yosys'srcattribute. - A 1-bit enum declared in a submodule or at file scope: the bench named the item in top's scope ("Unable to bind wire/reg/memory" for
eqv_rtl.OFF). It now reaches the item through the variable,s.first()ors.last()(a 1-bit enum has at most two items), whichever has the board's value, so where the enum is declared does not matter. (Round 4 found that walking to an item with.next()through a hierarchical name stops vvp;.first()and.last()alone run.) - A register yosys moves into a ROM. The corpus Lab 4 Gray counter (a full
case, aclearand apresetnot namedbtn*) becomes 6 FDREs whose power-up state holds the first state one clock longer; the RTL, started at the board's 0, is one step ahead, and the bench counted 9 differing bits (EQV MISMATCH at 9000 (posedg): q[0] rtl 1, netlist 0). A register without a start value promises no start. The bench now runs a second copy of the RTL, started asdewfpga simstarts it (x, until thecasedefault), and a compare where the netlist differs from the first copy passes when every bit the first copy knows equals the second: the board then shows what the student's own simulation shows. The excuse is per compare, never per bit, and anxin the second copy excuses nothing. The corpus shape now:EQV PASS: 36864 output bits compared, all equal (at 9 compares to the RTL as dewfpga sim starts it, without start values, not as the board does).
New cases
regslice,alias,enum1subandromstart: red on round 5'seqv.pyandeqv.sh,EQV PASSnow.- A wire that names a slice of an output register (
Parts of the netlist stage no case pinned. Each of these left all 37 cases green on round 5's tree and now turns at least one red: the start of a multi-bit enum (
enumstart: a netlist that powers up in state11, which the RTL never has), of a memory (memstart: a LUT RAM that powers up all ones), the compare after the switches change and before the clock edge (datasw, the switch counterpart of round 5'smealyb),eqv.sh's fail-closed guard (nobench: a bench that does not compile while the RTL does isEQV FAIL, exit 1, never a skip), the packages-first order (pkgfile: a package in a file that sorts after the design), the "nothing compared" fail (nothing), and the fallback to the name the always block writes (alias,regslice). The new excuse is pinned both ways: without itromstartis red, per bit instead of per comparemixstartis red (a netlist that takes one bit from each start), and with anxthat excusessubfault,enumstartandmemstartare red.eqv_check.shhas 48 cases;test/run.shruns all 48.Runner and suite guards no check pinned, each pinned now by a check that breaks it on a copy: a row's second quote (
st_quote2:04's dual-edge advice changed in the product) and its secondfile:line(st_line2:76's second line made wrong in the table; the breakers counted 24 and 17 rows with more than one on round 5's table); aneqv.shfailure other than exit 2 (st_eqvexit:07's bench made not to compile has to be a netlist fail; the runner's-eq 2could become-ne 0with every test green); the re-derived verdict's exact mode (st_exact, with the measured yosys and iverilog: a row that saysokwhereexpect.tsvrecords a netlist fail;sv_rowstakes the table as an optional argument for it); every word of the student-name check (st_namesplants a quoted name after each of its 10 words; it planted 3 forms).Probes that pinned nothing the CLI could lose. No probe's design had two files that reach
simandbit, so a Makefile that compiles only the first design file left every test green:91is a submodule in its own file, the usual lab layout. No probe's$displayreached synthesis (40and40bstop at the scanner,81's module is taken for a testbench), so droppingdelete t:$printleft every test green:92is a debug print in a clocked design.44's do-while could not tell do-while from while (its condition is true on entry): it now has a second loop whose condition is false on entry, and a reader that tests the condition first fails its testbench.Vivado supports four more things we fail, and no probe had them. Each is a probe now, with its source. Every new testbench of this round passes on a correct design (iverilog on the RTL) and fails with its construct broken (10 edits, 10 caught; list in the evidence).
89logic on a divided clock. The course's master XDC arrives withcreate_clockcommented out, and the CLI then checks every clock at 100 MHz: four decimal digits of a 14-bit value on the slow clock reach about 35 MHz, andbitstops with "timing not met". 11 routed Vivado timing reports in the corpus (2015.2 to 2021.2), each next to the.bitits run wrote, list a register clocked from a register under "no clock driven by root clock pin" and do not time it; with nocreate_clock, the board clock is listed there too. Withcreate_clockuncommented, nextpnr checks the divided clock at its default 12 MHz and builds the breaker's version. The same rule stops a button used as a clock (posedge btnC, noclkat all; measured, not a probe of its own).90initial if (N < 8) $warning(...), UG901's own form of a parameter check (Table 27 p.286; the report said p.285): yosys "Can't resolve task name" for$warning, and for$infothe same. The form withoutinitial, and theinitialform while its condition is false, build.22fan interface without ports whose members top drives and reads (bus.a): 0 LUT, exit 0, whiledewfpga simis right. It shows that22cand22dfail for the same reason, a member of an interface instance used in the module that instantiates it, not for their ports or their array.22c's row says so now, so a #3 fix aimed at "ports in interfaces" would not pass it.56ba file that holds only file-scope typedefs (UG901 'Compilation Units', p.270): the CLI hands the tools only files with a module, and both stop at the first use indesign.sv, naming neither the missing file nor the fix. In which order Vivado compiles such a file is not in the guide. The README, its Turkish copy, the cli page and the guide said only a package or an interface alone in its file is left out; they now say a file that holds no module.- Two whose Vivado status no source settles, recorded as unverified.
63ba real literal in a localparam used in an expression (localparam MAX = 1e1; ... count == MAX - 1; a lab writes50e6): yosys 0.69+post segfaults, and bash prints the crash and the Makefile's whole awk program (1.4 kB) with nofile:line;parameter realtimes a signal, andint'of alocalparam real, crash the same way.37blogic [3:0] sum = sw[3:0] + sw[7:4];:dewfpga simfollows IEEE 1800-2017 6.8 (an initializer, set once, sox),dewfpga bitbuilds an adder, and neither says a word. Its testbench checks the standard's reading, so it is a silent wrong whatever Vivado does.
Rows and sentences #2 shows false.
33said "the inout read-back is lost". Reading back a bidirectional pin works (33'sJA[0], and33b); what is lost is a pin the design sets to a constantz(assign JA[7:1] = 7'bz) and then reads. The row, these notes and guide section 8 say so now.82: with$fopengone,$fdisplaystops yosys next, not$fwrite.59builds with nextpnr's generic "No clocks found in design" warning, not with no warning.08c's comment saidantakesoutput logicfromseg; its own source says a strict reader makes it a net.- The cli page and guide section 5 said the course's seven-segment file "builds as it is". The course also hands
SevSeg_4digit.svout in netlist form (row57; the corpus holds 3 netlist-form copies), which the CLI refuses; both now say so, and section 7 names it among the netlist-form modules. The error pageport-neither-input-nor-outputsaid the fix is skipped when "a comment sits inside the port list"; the course file has three comments there and builds. It now names the two cases that miss (a comment betweenseg,andlogic dp, a;inside a comment), as section 7 does. - The why page said each construct sits "next to what Vivado's documentation says"; 10 rows cite only a Vivado log or IEEE 1800. It now says "what Vivado does and the source for it". It also called clock dividers "the part this chain handles"; it now names
89's difference. Guide section 2 said section 7 lists "what old student repos hit" and section 8 said "what the course's own files hit"; both say the course's files and old student repos now. - The README, its Turkish copy and guide section 2 said
bitrefuses to write a bitstream when timing is not met, and nothing about what is timed. They now say that withoutcreate_clockevery clock is checked at 100 MHz, a divided one too (89), and that a multiplier in a DSP48E1 with a register inside is not timed at all (below). - Guide section 8's numbers are this round's: 77 of the 105 Vivado-supported constructs fail a stage (55 stop before a bitstream, 12 fail only in
sim, 1 has no model, 9 build a wrong bitstream). The guide's HTML and PDF were rebuilt (13 pages); the desktop design is untouched, text only. - These notes: the
-noautowirelist named 6 of the 9 probes it changes (above), and did not say that22, legal code, stops building with it.
Tried, and dropped:
- Leaving the RTL's registers
xinstead of starting them (round 3's first reading):32'scaseon an unset register takes its default branch in the RTL and not on the board. Hence two copies of the RTL. - Excusing a difference bit by bit, each bit equal to either copy:
mixstart, a netlist that takes bit 0 from one start and bits 2:1 from the other, passed. The excuse is per compare. - Quoting the crash in
63b's row: the YoWASP build of yosys 0.68, CI's version, does not segfault; it stops at an internal cell check, and the row failed there (expect.tsv quotes 'Segmentation fault: 11', no stage printed it). The row names the crash without quoting it. - A probe for the DSP timing (break 2): every stage passes, so the runner would count it
ok, and no stage compares timing with a real delay. It is left with its fix. - A probe for two
assigns to onewire(break 8): itsvivadocolumn would need a source for what Vivado does (the breaker says it refuses a multi-driven net; no log here shows it), and the message is #5's.
Found and left to later updates (reproduced, not fixed here):
- #3: the silent wrongs
22fand37b. Paths through a DSP48E1 with a register inside are not timed: three multiplies and an add in one clock printPASS at 100.00 MHz(282.97 MHz,DSP48E1: 5/120, no DSP cell in the critical path), while the same logic in LUTs reaches 34.04 MHz; nextpnr-xilinx 3fd7878'sgetPortTimingClassreturnsTMG_IGNOREfor a registered DSP48E1. - #4:
56band90;89's timing rule, with75's (Vivado writes the bitstream; the owner decides);63b, if Vivado builds it. - #5: any yosys crash prints the Makefile's whole awk program and no
file:line(63b); twoassigns to onewirestop in nextpnr with "Net 'y' is multiply driven by cell ports ..." about internal cells, nofile:lineand no fix (forwire y,dewfpga simshowsxand passes; forlogic y, iverilog names the line). - #6: with two clocks the board clock is printed as
$abc$...$aiger$oNon the pnr line and in the timing error (32:$abc$2758$aiger$o7 399.20 MHz), next to round 2's garbled name of03. - #12:
dewfpga simwith two design roots saysName it: dewfpga bit decoder(check_xdc.pywritesbitfor sim too);dewfpga sim decoderworks. The plan lists it with top selection in VS Code.
Break round 7. The same three kinds of breaker ran against the tree after round 6 and sent 21 reports. Each was run in a scratch folder: 20 reproduced as written and one in part (the port-line comment, below); a mutation report by its mutation and the cases or checks that could catch it, not by a full suite run each. Three side claims did not hold (below). A first attempt at this round stopped before it changed anything (a network outage). The eight probe folders the task named as lacking rows (13c, 18c, 21b, 22c, 22d, 22e, 64b, 81) have had their rows since round 4, as round 6 found too: 123 folders and 123 rows before this round, 132 and 132 after. Fixed here:
- The netlist stage passed wrong netlists through round 6's sim-start excuse. A compare passed when every bit the RTL knows equalled a second copy of the RTL started as
dewfpga simstarts it, decided compare by compare. A digit-select counter with no reset and no start value (a lab's display scan) showed the hole:dewfpga simkeepsselatxforever, itscasetakes the default at every compare, and a product netlist with one LUT INIT bit flipped (digit 0 never lights) equalled the board's start at some compares and that copy at the others:EQV PASS: 196608 output bits compared, all equal (at 3072 compares to the RTL as dewfpga sim starts it, ...). A netlist that lost the counter and shows 1111, what sim shows, passed the same way. The exception is now one decision for the whole run: the netlist has to follow the RTL as the board starts it at every compare, or follow the sim copy at every compare, equal to the board's start while a register bit of the sim copy is stillx(its outputs then come from a branch no board takes) and equal to the sim copy, every bit known in it, once none is. Both netlists:EQV FAIL: 3072 of 196608andEQV FAIL: 9216 of 196608. New casessimflipandsimlost:EQV PASSon round 6's bench,EQV FAILnow. - A reset on a switch was not pressed before the compares. The bench held only
btn*inputs for one clock edge first, so a state machine yosys re-encoded one-hot passed with its reset onbtnCand failed with the same reset onsw[15](EQV FAIL: 10 of 196608, all before the walk first raisedsw[15]). The bench now also holds every input bit that resets a register of the RTL (theSRSTandARSTof the flip-flops yosys makes of the RTL, when they are an input bit), at its active level. The corpus Gray counter round 6 made the excuse for (itsclearandpresetare switches) now passes without it. Five cases pinned a register's start through a switch reset (xstart,fillset,fillaset,xstarta,fillasym); pressed first, that start no longer shows (with the reset value ignored, all five stayed green), so their reset is now built from two switches,sw[15] & sw[14], which the bench does not press, and the reset-value mutation turnsfillset,fillasetandfillasymred again. New caseswreset:EQV FAILon round 6's bench,EQV PASSnow. - The bench did not compile for an output that is part register, part
assign(always_ff ... led[3:0] <= ...; assign led[15:4] = sw[15:4];): the start wroteledwhole, iverilog refused it (Cannot perform procedural assignment to variable 'eqv_rtl.led' because it is also continuously assigned.), and a correct netlist gotEQV FAIL.eqv.shnow reads the names iverilog gives there, andeqv.py --bitswrites the bench again with those registers started one bit select at a time (a bit select of a packed array of more than one dimension picks an element, so such a variable fails the bench by name instead). iverilog shows such a register bit aszbefore its first write, notx, so the start takeszfor unset too. New casepartassign: red on round 6's bench,EQV PASSnow. - Parts of the netlist stage no case pinned, each pinned now: "an
xin the sim copy excuses nothing" (dropping it left all 48 cases green;xhold, a RAM never written read at a registered address, where the netlist readsxand so does the sim copy), andSTARTS_AT_1'sFDSE_1andFDPE_1(negset, falling-edge registers with a synchronous set and an asynchronous preset). No probe had a falling-edge register either:31bhas both, with resets built from two switches, so the start shows.eqv_check.shhas 54 cases;test/run.shruns all 54. 13d's testbench missed faults. It is the only check of13d's netlist (iverilog cannot compile the RTL), and it clocked the grid in and picked the row and column with the same switches, so 16 LUT INIT flips that change the netlist passed it. It now clocks the grid in from one value and picks the row and column with another (the 256 patterned values, then 19744 random pairs). Over all 432 LUT bits of the product's netlist: 414 caught (the breaker counted 308 with the old testbench). The 18 that pass all sit in the LUT that drivesled[12], and they are exactly the 18 input combinations of that LUT that never occur over every grid value and every row and column (1048576 cases), so none of them changes what the netlist does.- Runner and suite guards no check pinned, each pinned now by a check that breaks it: the rtl stage's exit-code half (
st_rtlexit:07's testbench made to call$errorand still printPASS;st_tbfailpinned only thePASShalf); a missing score line (sv_rowscompared the line only when there was one;st_scorenow also gives it a log with no score line and one that writesSynthesis, and each has to fail); the personal-path check, which skipped all oftest/run.sh. It now scans every file: a home folder is/Users/and a name,test/run.shwrites its planted paths throughprintf's%s, and a new check plants one intest/run.shitself. - Constructs no probe had. Each is a probe now, with its source; every new testbench passes on the RTL (iverilog) and fails with its construct broken (evidence below).
93two modules nothing instantiates, in one file named after one of them. When two roots qualify, the scanner takes one named like the.xdcor like its own file without saying so:counter.svholdingcounterand the board versiontopbuildscounter, exit 0,counter.bit, and with one design file the CLI prints notop module:line. Saved aslab4.sv, the same file stops and names both. Two corpus Lab 4 projects have this shape (Vivado 2015.2; each.xprandrunme.lognames the board version as the top): the CLI alone builds the plain counter of one (061,pnr ok: 6 LUT, 6 FF), and refuses the other (058), where the corpus harness then named the plain one. Guide section 2 said "names both and you choose"; it, the README, its Turkish copy and the cli page name the exception now.94a variable raised to a constant power,sw[7:0] ** 2(UG901 p.278: "A**B is supported if A is a power of 2 or B is a constant."): yosys leaves a$powcell, nextpnr has no place for it, and the CLI says the design does not fit.16'd3 ** sw[2:0], which the note does not cover, stops the same way;2 ** sw[3:0]andsw[7:0] * sw[7:0]build.96a one-bit vector port ([N-1:0],N = 1) and96ba port numbered from 1 ([4:1], with the lab's own XDC; the runner now takes a probe's ownBasys3_Master.xdc): the CLI's XDC check names a one-bit vector port without its index and numbers every port's bits from 0, so it stops before place and route with "these ports have NO pin" and a fix that does not apply. nextpnr-xilinx places both right when run on the product'stop.json(ledandswonled[0]'s andsw[0]'s sites;sw[1]tosw[4]on theirs).97UG901's(* ram_style = "block" *): yosys builds a RAMB18E1 despite-nobram, nextpnr does not time it (pnr ok: 0 LUT, 0 FF, no clocked paths, timing not applicable), and yosys'cells_sim.vhas no model for it, so the netlist readsz. It counts with35as a bitstream the suite cannot check.(* rom_style = "block" *)on an initialized ROM gives a RAMB18E1 the same way (measured, no probe). README, its Turkish copy and guide section 8 said every memory becomes LUTs; they name the exception now.- Three whose Vivado status no source settles, recorded as unverified:
95a submodule with ports and no body whose output top uses (yosys makes it a blackbox, and the CLI says the design does not fit);98a module namedINV(GND,VCCandMUXF7too;BUFbuilds): yosys stops in its owncells_sim.v, with no line of the student's file;99parameter string INIT_FILE = ...: a yosys syntax error with no fix, while the untyped form builds and matches the RTL.
- Rows and sentences #2 shows false.
- "14 silent wrongs" (these notes, the plan):
expect.tsvhas 15 rows where the bitstream builds and the netlist fails, and the runner counts all 15 alike. The 15th,35_mmcm, fails for lack of a model, not because it is wrong. The notes now name it; with93and97there are 17 such rows: 15 silent wrongs and 2 bitstreams no model can check. - "8 build" (
corpus_results.json, in round 3'sCLAUDE.mditem and round 1's list below):dewfpga bitalone built 2 of the 89 folders (004,061); the other 6 built only after the corpus harness named a top the CLI had refused to pick, and 2 of the 8 (058,061) are not the module the student's Vivado project built.CLAUDE.mdand both places here say so now. 22f's source said UG901's p.282 example uses an interface's members in the module that instantiates it; the example uses them only inside the submodules, through their interface port. The row says so, and rests top's own use on Table 27's "Hierarchical names: Supported" (p.288).- The why page gave each construct's source as "its user guide, a Vivado log, or the language standard";
04and05cite forum posts, marked weak. It names them now. - Guide section 7 said the port-line fix misses "a comment inside the port list (after
seg,...)". It misses a comment betweenseg,andlogic dp(at the end of theseg,line, on a line of its own, or a/* */between the two) and a comment anywhere in the port list that holds a;; a comment afterlogic dp,or before the comma is fine. The same sentence quoted the yosys error in backquotes that broke the guide's HTML (the rest of the paragraph rendered as code, up tobit). Both are fixed, and the guide's HTML and PDF rebuilt. The error page's sentence ("unless a comment sits betweenseg,andlogic dp") holds. - Guide section 8's numbers are this round's: 82 of the 111 Vivado-supported constructs fail a stage (58 stop before a bitstream, 12 fail only in
sim, 2 have no model, 10 build a wrong bitstream).
- "14 silent wrongs" (these notes, the plan):
Side claims that did not hold:
- The port-line report's "a comment after
seg,on the same line is fixed": only because the breaker's comment ended in a comma (// a comment after seg,), which the fix reads as the port separator; with// the segmentsthere the line is not fixed (evidence below). - "Vivado 2020.1 logs in the corpus warn on an undriven net ([Synth 8-3848])": the two corpus logs with it are Vivado 2015.2 and 2021.2.
94's report put UG901's "Power operator" on p.227-228 and read it as supported without condition. The Verilog-2001 list that holds it is on p.229, and the conditions are Ch.10's note (p.278) and Table 25 (p.249): a variable base with a constant power is supported,3 ** swis not.94probes the first.
Tried, and dropped:
- Starting a part-assigned register with
forceandreleaseon the whole variable: iverilog 13 left the register bits atzafter the release ("procedural continuous assignments are not yet fully supported"). Bit selects work. - Choosing the variables to start bit by bit from their declaration's text (how many packed dimensions it has): a comma list,
logic [1:0][3:0] a, b;, hides them fromb. iverilog names exactly the variables it refuses, so the bench is written again with those. - Keeping the excuse per compare and requiring only every bit known in the sim copy: the flipped digit counter's sim copy has no
xoutput at any compare (thecasedefault is a constant), so it still passed. - Pressing only
btn*and letting the switch-reset state machine fail: round 4's reason for the press (a student presses reset after loading the board) holds for a switch too. - Sweeping
13d's 432 flips against every grid value and every row and column, one netlist copy per flip: 432 copies times 1048576 cases was too slow for this machine; the 18 survivors were instead checked by recording which input combinations of their LUT ever occur over the same 1048576 cases (7 s).
Found and left to later updates (reproduced, not fixed here):
- #3:
93, the tie-break that builds another module than the project's top with no message (the CLI has to stop and name both, or say which it took and why); the block RAM of97is not timed, next to round 6's registered DSP48E1. - #4:
94(the$powcell),96and96b(the XDC check's port names);95,98and99if Vivado builds them. - #5: "the design does not fit" for
94and95(a$powcell, an empty module's blackbox: neither is a size problem), the same message's "with -nobram every memory becomes LUTs" (false for97),98's error insidecells_sim.vwith no line of the student's file and no fix,99's syntax error with no fix. - #6: a falling-edge design's clock named on the pnr line by a cut internal name (
31b:pnr ok: 11 LUT, 5 FF, 337:slice$1535.C': 358.68 MHz: the awk cuts the name at its first colon), next to round 6's garbled names.
What the breakers tried that held. Every round, the breakers also reported what they tried and could not break. Those runs are part of the evidence, so they are listed here, round by round.
- Round 1.
- The runner's numbers: the full probe run and
test/run.shgave the score line and the stage counts these notes then printed (rtl 35/8, synth 32/11, netlist 28 pass, 4 fail, 11 not run, bit 32/11; Vivado column 36, 2, 5), under macOS's/bin/bash3.2 too. The old runner's numbers (33/43, 35/43, 29/33) match its saved result file. - Mutations the suite caught: every LUT INIT set to 0 (19 probes fail at the netlist stage; the other 9 have no LUT cells),
--fix-portsoff (08),create_clockuncommented (the warning quoted above),-g2005, an expectation flipped, a probe folder without a row, malformed rows, the header renamed, the netlistPASScheck dropped (05and33BAD with the measured yosys),--prefer-tbremoved from the CLI (26BAD), a home-folder path planted in a probe (the hygiene check FAILs). All 8 flip-flop INIT flips in37's netlist were caught. - Sources: the UG901 v2023.2 and v2022.2 and UG953 v2022.2 pages cited then were opened and hold (one breaker downloaded UG953 from xilinx.com; its sha256 equals the copy used here), and so do the corpus Vivado log quotes of
01,02,07,08and32.03,03b,04and05staying unverified is right: no UG901 text on them, and no corpus log with Synth 8-91, 8-6859 or MDRV-1. - Designs a student writes that passed all four stages: a BCD converter with
/and%(every value 0 to 9999), bits of one output from twoalways_ffand analways_comb, Verilog-2001 non-ANSI ports withdefparam, a net declaration assignment and{cout, sum}on the left, a RAM filled by aninitialloop and then written, a blocking temporary insidealways_ff, alocalparamin the parameter port list, a compilation-unittypedef structandenumas port types, a function with a loop and a local,/and%by a variable. Twoassigns to one wire stop in nextpnr (not silent; the message is #5's). - 28 corpus testbench pairs gave the same waveforms on the RTL and the product's netlist; the 3 differences were testbench races (stimulus on the clock edge) or
xat time 0. The sweep stopped at folder 39, where one VCD reached 532 MB on this 8 GB machine. - The product did not change (
bin/,templates/,sim/equal to HEAD), and the 89 corpus lab folders give the same results before and after: 8 build (2 bydewfpga bitalone, 6 with a top the harness named; round 7), the same error lines, only their order changed in two folders (the Python set, above). - CI's log of the last pushed commit shows yosys 0.68+post and Icarus 13.0; the 43 match under the YoWASP build of 0.68. The Homebrew bottle itself was not run here.
- Privacy: no corpus owner name, no home-folder path;
06is a reduced rewrite of a lab, not a copy;ci.ymlparses and the npm package is unchanged (test/is not in itsfiles).
- The runner's numbers: the full probe run and
- Round 2.
- Designs that passed every stage, the equivalence check included: a full-case
always_combwithout a default,unique casewithout a default, an unpacked array with a pattern initializer andassign segs = '{...},$countones,$onehot,$onehot0, a default port value left unconnected, a packed union,const, a reallocalparamconverted to an integer, a file-scope enum whose names do not clash, a file-scope function, a$readmembROM from a.txtfile with asynchronous and synchronous reads, a CRLF XDC and a CRLF source with the course'sdpline, the course's exact seven-segment port line with its trailing comment. A signal declared and never assigned gets yosys' "used but has no driver" warning (Vivado warns too, Synth 8-3848 in 2 corpus logs; the message has nofile:line). - The Harris and Harris testbench style (
$readmembvectors,assert ... else $error) reports a mismatch with itsfile:lineand exit 2. 3 real corpus labs that build (a Gray counter, a multi-digit display, a traffic-light FSM):eqv.pyPASS against the product's netlist. 21 corpus xsim elaboration logs, each module set run throughdewfpga sim: only the known classes failed (38,34, a testbench without$finish, two roots). - Mutations the suite caught: the netlist stage without its
PASScheck (39), the unnamed-instance line off by one (02,40),exact=0, regressions turned into notes under yosys 0.68 (st_stale), the row-count check removed with a malformed row, the old personal-path grep,--fix-portsoff, every LUT INIT 0 (19),-g2005(37 of 51), the sim built with-s $(TOP)(07,09), a broken16c. 60 LUT flips over13,15,21,29,32: 58 caught; the one29survivor also passes 65536 cycles. - The regression breaker ran HEAD and the tree side by side: 51 passed before; 86 passed with 23 gaps after, all 51 old checks among them; the corpus identical but for the error order in three folders. They recounted the table, re-ran
30's LUT flip (the same two lines), the source mutations (122 killed, 32 survived) and 372 distinct LUT flips of their own (42 survive, every one explained above), and re-read the UG901 and UG953 pages and the corpus quotes: all hold.test/run.sh's install check points the Homebrew link at whichever copy runs it (known; restored after every run).
- Designs that passed every stage, the equivalence check included: a full-case
- Round 3.
- Designs that passed sim, bit and the equivalence check: a declaration in an unnamed
beginblock,#(W = 4)without theparameterkeyword, alocalparamin the header list,repeat (2), C-stylelogic [3:0] mem [16], a packed union,$countones,$onehot,$onehot0,$countbits,$high,$low,$size,$left, a task with output arguments, a file-scope packed struct as a port type, an implicit net in a port connection,byte,shortint,int unsigned, default port values,const, a struct bitstream cast, a positional pattern'{a, b}, a constant function in alocalparam, a function that reads a module variable insidealways_comb,defparam,initial if (W < 1) $error(...),unique0 case,wire logicandvar logic. Blocking and nonblocking assignments mixed on one variable inalways_ff: EQV PASS. 37's power-up values reach the bitstream: the FASM's ZINI bits match the 8 flip-flop INITs. A combinational design withcreate_clockuncommented only warns and writes its.bit. Immediate asserts that fail in a testbench exit 2 with thefile:line.- The equivalence check on all 30 probes whose netlist then passed: 25 PASS, and the 5 skipped are the ones the notes named. Every output bit of the 25 is compared at least once (a per-bit counter). The slowest bench runs 4.5 s against the runner's 30 s limit. Corpus: 38 lab tops EQV PASS; two mismatches come from registers the RTL leaves
x, judged not synthesis faults but not proved cycle by cycle. - Mutations the suite caught with the measured yosys: the comparison removed (
03), the netlist'sPASScheck dropped (39), no button presses (03). - The regression breaker: the corpus through HEAD and the tree with a fixed hash seed, 0 differences; 99 passed with 41 gaps, all 51 old checks among them; 69 of 69 under yosys 0.68 with the same rows; every quoted message found in the stage logs; the UG901 and UG953 pages and the corpus quotes of
01,38,53and54hold; round 2's testbenches of41,43,47,49and50walk every input and tell the construct from its misreading. Not re-run by them: rounds 1 and 2's source-mutation counts, the 236-flip totals, and which synthesis run57's bitstream came from.
- Designs that passed sim, bit and the equivalence check: a declaration in an unnamed
- Round 4.
- All 92 probes under yosys 0.68 (the YoWASP shim) and natively: 92 of 92 match, the same score line (11 min 49 s and 6 min 31 s). Only
32has start-value differences. The slowest bench takes 6 s. - Constructs that passed all four stages:
const, a packed union, an array pattern assigned to a variable, a default port value left unconnected, a bitstream casts_t'(v),$countones,$onehot,$onehot0,$countbits,$size,$high,$low,$left,$right,$dimensions,$unpacked_dimensions,$increment,defparam,repeat (3),`default_nettype none,$errorin aninitialon a parameter,$fatalin a generateif,bufif1to a pin, a constant function in alocalparam, alocalparamin the parameter header, an enum variable without a typedef, a packed 2-D port,$rtoi($ceil($log10()))and$sqrtin parameters,16'(2 ** sw[3:0]),/and%of two variables,always_ff @(negedge clk)(FDRE_1 and CLKINV in the FASM), a RAM filled by aninitialloop,$readmemb("rom.data"). - Corpus: every lab folder through
dewfpga bitandeqv.py: 21 EQV PASS, 2 start-value artifacts (a ROM default ofx, a counter with no start value clocking a register that has one), 1 with no known output bit; no synthesis fault. - Mutations that changed no verdict then and were not reported: FDSE and FDPE not started at 1 (the old excuse hid it; round 4 then removed the excuse), the negedge compare dropped, the "nothing compared" guard dropped (pinned in round 6). Caught: 256 cycles (
dense,sparse), the port-line fix taking the first direction (08), the CLI ignoring.vfiles (74). A reset tied off in each flip-flop with a driven reset or set pin (55 faults over 21 and 12 clocked probes, 37 of which change the board): the testbenches catch all 37; only the equivalence check missed them (the round's high break). No false fail on33bor on a combinational design with buttons. - The regression breaker: 119 passed with 56 gaps; one summary line; the corpus unchanged but for one folder's error order; the table, the arithmetic of the notes and the 0.68 rows recounted;
75reaches 33.61 MHz; the UG901 pages of52to80and the v2022.2 cites of13b,42,48and66hold; slang's table names 9.4.2.3 and its test isalways @(a iff enable == 1); the corpus quotes of53,54,75,76and77hold; nextpnr-xilinx writes anxbit of a LUT INIT as 0;git ls-fileslists all 193 probe files (none ignored, so CI gets them); no corpus owner name, checked one at a time.
- All 92 probes under yosys 0.68 (the YoWASP shim) and natively: 92 of 92 match, the same score line (11 min 49 s and 6 min 31 s). Only
- Round 5.
- Designs that passed every stage:
$countones,$onehot,$onehot0,2**sw[3:0],/and%by a variable,===and!==,$size,$left,$right,$high,$low,$rtoi($sqrt),$ceiland$powin localparams,defparam,repeat (8),assign #5,<= #1,and #2; signed/,%,>>>,*, compare and**on all 65536 inputs (equal except divide by zero, where the RTL isx);$floor,$ln,$log10,$exp,$sin,$cos,$atan2,$hypot,$asin,$countbits,$increment,$dimensions,$unpacked_dimensions; a default port value, a packed union,const, a bitstream cast, an enum in a numeric expression, a constant function; twoalways_combsharing a module-levelinteger; aninitialloop RAM with a later write; an output port with an initializer; register-file writesmem[a][3:0] <=andmem[a][b] <=; an unconnected submodule input (0, as in Vivado); an ascendingoutput logic [0:6] seg(the routedseg[0]lands on W7, as the XDC says); a button as a clock, with and withoutCLOCK_DEDICATED_ROUTE FALSE; a gated clock; a testbench that ends in$stop; the Harris and Harris testbenches; aninouttristate in a submodule onJA. A non-constant$clog2stopsbitwith afile:line; what Vivado does with it is not verified. - Corpus:
dewfpga simon 55 testbench targets, 18 pass, the rest the known classes or student bugs; 36 designs EQV PASS against the product's netlist. The Gray counter one clock behind after power-up was noted and not reported then; round 6 fixed the bench for it. - Mutations:
eqv.pywith a binary walk, reset values ignored,$sdffnot started, no button presses after the start, the reset press without a clock edge, 1024 cycles, 15 exhaustive bits, pins never driven, only the RTL's 1 bits compared, enum starts off, a latch started at its preset, modules not renamed, every flip-flop INITxset to 0: each killed by a case. No data compare:33bfails. Equivalent or dead, not reported:hdlnameignored, the all-0 and all-1 modes dropped, one edge's compare dropped, 4001 cycles, a button rate of 1/2. Not verified then: FDSE_1 and FDPE_1 entries, the memory start, theassigned_inbypass (round 6 pinned the last two). - LUT flips in the 10 probes whose comparison is skipped: the testbenches catch every flip that changes the netlist (
55's 11 and73's 3 survivors each match the unflipped netlist). A source-operator sweep over the 62 probes whose rtl passes left only equivalent mutants and known cases. - The regression breaker: 138 passed with 63 gaps (791 s at a load of 17 to 27); the rows equal the table; 100 of 100 under yosys 0.68 with the same rows; the timeout margin (slowest 3.53 s); the UG901 cites of round 4's rows; nextpnr's
fasm.cclines 811-825; the corpus quotes of53,54,57and75;81and26reproduced; the guide's HTML equal to a rebuild; no names; the 25 corpus files with the course'slogic dpline all read after--fix-portsexcept one whose module name starts with a digit (invalid anyway). - Not verified by them: what Vivado does with a typedef header included by two files, with
output logic Cout; reg Cout;, and with a testbench that assigns a variable a DUT output drives.
- Designs that passed every stage:
- Round 6.
- Designs that passed:
CLOCK_DEDICATED_ROUTE FALSEon a button clock; a CRLF XDC withcreate_clock(16 of the 37 corpus XDCs are CRLF);create_clockin a design with noclk;const,union packed,unique0 case, a default port value,defparam,var logic, an enum in arithmetic; a constant function withoutreturn(with it, the16bclass);initial if (N > 8) $error(...)while false; an unconnected submodule input; a function reading a module variable inalways_comb; vector bits driven fromassignandalways_comb, from twoalways_comb, fromalways_ffandassign; registered 16x16 and 32x32 multiplies (their timing is the DSP break above); negedge registers read by posedge ones (CLKINV); an ascending[0:6]port (13 corpus files use it);$readmemhwith a Windows path (both stop withdesign.sv:3); a gated clockclk & sw[15]; the Table 21 math functions in localparams. A parameterized interface passed to a port builds right and fails in sim (22's class);initial q = $random;stops with nofile:line(77's class); a top in two files is the known two-files class. - Corpus: 31 lab folders (41 roots) through
dewfpga bitandeqv.sh: EQV PASS except the Gray counter and the dot-matrix project (both fixed this round), one empty stub and 12 skips (38's class, redeclared ports). Testbench constructs$urandom_range,$sformatf, queues, dynamic arrays,fork/join,wait,finaland.name()pass sim;%p, associative arrays andstd::randomizefail in iverilog (0 corpus uses). - Mutations caught: memory words started at 1 (
28), the data compare removed (33b). Equivalent or dead today: one edge's compare, LUT or RAM INITxset to 1, theINIT_prefix,upto, the start delay, thetopattribute (all 200xINITs in the 57 product netlists are FDRE, FDCE, FDPE or LDCE). The newer testbenches (41to88, 18 read) each tell the construct from its obvious misreading. - The regression breaker:
bin/andtemplates/equal to HEAD; 51 passed before, 148 with 77 gaps after; the corpus the same (the three folders whose error order differs also differ between two runs of the same CLI); 115 of 115 under yosys 0.68; the table's counts, the probe layout (one design file withmodule top, atb.svwithmodule tb), the corpus logs of08cand13d, the UG901 rows of the round-5 probes, and the guide's HTML (equal to a rebuild) and PDF (13 pages) all hold.
- Designs that passed:
Tests. test/run.sh: 51 passed, 0 failed before #2 (78 s); passed 94, failed 0 after #2's first draft (228 s). After break round 1: passed 86, failed 0, known gaps 23 (356 s). After break round 2: passed 99, failed 0, known gaps 41 (305 s). After break round 3: passed 119, failed 0, known gaps 56 (409 s). After break round 4: passed 138, failed 0, known gaps 63 (556 s). After break round 5: passed 148, failed 0, known gaps 77 (474 s). After break round 6: passed 166, failed 0, known gaps 82 (533 s; the final run of that round, 528 s, the same). After break round 7: passed 178, failed 0, known gaps 87 (583 s). The 178 are the 51 from before, 4 hygiene checks, 24 checks of the probe runner itself, 54 checks of eqv.py and eqv.sh, and the 45 probes that are not known gaps (round 4: 63 stopped being a gap and the 8 new probes were all gaps; round 5: of the 15 new probes, 14 are gaps and 84 passes; round 6: of the 8 new probes, 5 are gaps, and 63b, 91 and 92 pass; round 7: of the 9 new probes, 5 are gaps, and 31b, 95, 98 and 99 pass). The final run on 26 September, after these notes were written: passed 178, failed 0, known gaps 87 (582 s), the same (evidence below).
personal-path check sees test/sv/run.sh: a home-folder path planted in a copy'stest/sv/run.shhas to be found;personal-path check sees test/run.sh(round 7) plants one intest/run.sh, which the check skipped whole until then.no names from student code in expect.tsv: no quotedmodule '<name>',`\name'or(name_reg)in the source column;student-name check sees a quoted nameplants all three and has to find each (round 4 added the`\name'plant), and a quoted name after each of the pattern's 10 words (round 6).- The runner checks, each on a copy of the product with one thing broken, running one probe: a stale
top_simthat printsPASSnext to a broken09(the run has to sayrtl fail);05recorded as refused under a yosys that is not the measured one (the run has to report a new silent wrong);07's netlist recorded as failing (the run has to fail on the unexpected pass; only with the measured yosys);26with an empty source (refused); the CLI's latch error moved to line 1 (the run has to sayexpect.tsv says design.sv:3, no stage printed it). Round 2 added:16cbroken so its testbench printsFAIL(has to bertl fail); a probe folder with no row (refused); adewfpga bitthat exits 0 and deletestop.bit(has to bebit fail);01's column set tonot supported, thenunverified(both refused);14broken in the top finder, run through the runner andtest/run.sh's row check (the runner's row has to saybad, its exit code has to be non-zero, and the row check has to printFAIL); six hand-written rows (abadrow fails, and so do anokwhose stages differ fromexpect.tsv, anokfor a silent wrong, anokfor a supported probe that fails a stage, and an exit code that does not fit the rows; a clean row passes). Round 3 added, both under a yosys that is not the measured one:07with a netlist that differs from its RTL and a testbench that sees nothing (has to benetlist fail, expected pass), and54the same with its own testbench, where the comparison is skipped (the same); and a check that the runner checks write nothing toGITHUB_STEP_SUMMARY. Round 4 added: the score line counted again from two hand-written rows (the right line passes; each of four wrong numbers fails); the latch error's advice replaced (01has to sayexpect.tsv quotes 'Give q a default value at the top of the block.', no stage printed it); the CLI made to skip.vfiles (74has to sayexpect.tsv says design.v:3, no stage printed it). Round 5 added: the unnamed-instance fix replaced (02has to sayexpect.tsv quotes 'Write: inv u_inv(', no stage printed it: a quote in"...");01's vivado column writtenSupported(refused as a malformed row). Round 6 added:04's dual-edge advice replaced in the product (04has to sayexpect.tsv quotes 'Drive each signal from one always block.', no stage printed it: a row's second quote);76's secondfile:linemade wrong (expect.tsv says design.sv:9, no stage printed it);07's bench made not to compile (netlist fail, expected pass: aneqv.shexit 1 is a fail); and, with the measured yosys and iverilog, a hand-writtenokrow for07against a table that records its netlist as failing (has toFAIL). Round 7 added:07's testbench made to call$errorand still printPASS(dewfpga simexits 1; has to bertl fail); and, in the score check, a runner log with no score line and one that writesSynthesis(each has to fail). eqv.py: ...andeqv.sh: ...(54 checks):test/sv/eqv_check.shwith equal,msb,zero,walk,cbutton,ones,dense,sparse,deep,button,zout,xout,init,fdse,ldpe,ldfill,lostinit,xstart,fillset,fillaset,pkenum,enum1,lostreset,recode,hier,hierfault,names,ioequal,ioread,iofloat,mealyb,xstarta,xstartl,fillasym,subreg,subfault,regport,regslice,alias,enum1sub,enumstart,memstart,datasw,nobench,pkgfile,nothing,romstart,mixstart,simflip,simlost,xhold,swreset,partassign,negset(round 4 added 15 and changeddeep,button,xstartandrecode; round 5 added 7, and every case now runs the runner's owneqv.sh; round 6 added 11; round 7 added the last 6 and built the reset ofxstart,fillset,fillaset,xstartaandfillasymfrom two switches).unnamed instance -> line and fix(one of the 51) now also looks for the fix,Write: sub u_sub(; it looked for the line only (round 5).
The four stages today, before #3 and #4 change anything:
| rtl | synth | netlist | bit | |
|---|---|---|---|---|
| pass, the 43 from 2509 | 35 | 32 | 27 | 32 |
| pass, the 51 after round 1 | 37 | 35 | 28 | 35 |
| pass, all 69 | 49 | 38 | 30 | 37 |
| fail, all 69 | 20 | 31 | 8 | 32 |
| not run (no netlist), all 69 | 31 | |||
| pass, all 92 | 60 | 47 | 37 | 44 |
| fail, all 92 | 32 | 45 | 10 | 48 |
| not run (no netlist), all 92 | 45 | |||
| pass, all 100 | 62 | 52 | 39 | 49 |
| fail, all 100 | 38 | 48 | 13 | 51 |
| not run (no netlist), all 100 | 48 | |||
| pass, all 115 | 68 | 57 | 42 | 53 |
| fail, all 115 | 47 | 58 | 15 | 62 |
| not run (no netlist), all 115 | 58 | |||
| pass, all 123 | 75 | 62 | 45 | 57 |
| fail, all 123 | 48 | 61 | 17 | 66 |
| not run (no netlist), all 123 | 61 | |||
| pass, all 132 | 84 | 69 | 49 | 60 |
| fail, all 132 | 48 | 63 | 20 | 72 |
| not run (no netlist), all 132 | 63 |
- The 43: synthesis 32/43, all four stages 24/43 (25 before the netlist was compared with the RTL;
03moved), Vivado-supported probes passing all four 23/36. - The 51 after round 1: synthesis 35/51, all four stages 24/51, Vivado-supported probes passing all four 23/43, known gaps 24 (
13bbecame supported in round 2: 23/42 and 23 before). - All 69 after round 2: synthesis 38/69, all four stages 24/69, Vivado-supported probes passing all four 23/60, known gaps 41.
- All 92 after round 3: synthesis 47/92, all four stages 26/92, Vivado-supported probes passing all four 25/77, known gaps 56.
- All 100 after round 4: synthesis 52/100, all four stages 26/100, Vivado-supported probes passing all four 25/84, known gaps 63. (
63moved from supported to unverified: 25/76 on round 3's 92.) - All 115 after round 5: synthesis 57/115, all four stages 27/115, Vivado-supported probes passing all four 26/99, known gaps 77. The one new probe that passes all four is
84(a constantrepeat). - All 123 after round 6: synthesis 62/123, all four stages 29/123, Vivado-supported probes passing all four 28/105, known gaps 82. The new probes that pass all four are
91(a submodule in its own file) and92(a$displayin a design module);63bfails where Vivado is unverified. - All 132 after round 7: synthesis 69/132, all four stages 30/132, Vivado-supported probes passing all four 29/111, known gaps 87. The new probe that passes all four is
31b(falling-edge registers);95,98and99fail where Vivado is unverified. - The old runner (yosys called directly): synthesis 33/43, rtl 35/43, netlist 29/33.
- The 82 Vivado-supported probes that fail a stage are our gap (77 of 105 after round 6, 73 of 99 after round 5, 59 of 84 after round 4):
- synth, yosys refuses:
01latch in always_comb,02unnamed instance,11enum methods,12bassignment patterns,12can array of structs indexed by a signal,13bunpacked array port,13cunpacked array concatenation,16breturn,16cwhile loop,22ea modport expression,23/23bpackages,23ea package that exports a name,41==?,43foreach,44do-while,45break,47type parameter,49default argument,50argument by name,51unpacked array localparam,51bpacked 2-D localparam,53async reset with a synchronous clear,56a file-scope enum namedS0,60pass by reference,61enum ranges,62the type operator,64an unpacked struct,64ban unpacked union,65a streaming target,67array equality,68an array slice,69disable,82$fopen/$fdisplay/$fwrite(UG901: Ignored),83$realtobits/$bitstoreal,90$warningin aninitialparameter check,85a bitstream cast,86extern module,87aconfig,88a class's static function (02,12b,13c,22e,51,51b,60,62,64,64b,65,67,68,83,85,86and88fail rtl too); - the CLI's scanner stops sim and bit:
23dheader import,40a module nameddisplay,40ba module name and(inside a$displaystring,57a course module in netlist form,78UG901's keep_hierarchy attribute; - the CLI reads every file as SystemVerilog:
74a Verilog-2005.vfile; - the CLI leaves a file out:
22binterface file (rtl: iverilog syntax error; bit: yosys does not find the interface),23cpackage file (sim fails; bit builds a wrong bitstream),56ba file of file-scope typedefs (sim and bit stop at the first use, and neither names the file); - the CLI's top finder:
26; its port-line fix:08b; its XDC check:96a one-bit vector port,96ba port numbered from 1; - place and route:
52internal tristate ($_TBUF_),33ctristate buffers in submodules driving an output port ($_TBUF_),70wor/wand ($and,$or),94a variable base with a constant power ($pow); - timing:
75misses 100 MHz; Vivado writes the bitstream, the CLI does not.89logic on a divided clock, which Vivado does not time: with nocreate_clockthe CLI checks every clock at 100 MHz; - rtl, iverilog 13 refuses:
08ca port with only a range written in an always block (bitwrites its direction in,simdoes not),13da packed 2-D array indexed by a variable, then a bit or part select,18bunique if,18cunique0 if,21ban assignment inside an expression,22modport ports,38gate primitives drivinglogic,54a name used before its declaration,71a whole-array assignment,72a void function with outputs,73an array argument; - rtl, no UNISIM models in iverilog:
34BUFG,35MMCM (35's netlist too, no model in yosys'cells_sim.v); - netlist, no model in yosys'
cells_sim.v:97UG901'sram_style = "block"(a RAMB18E1); - netlist (silent wrongs): the members of an interface instance used from the module that instantiates it,
22c(with ports),22d(an array of interfaces),22f(no ports);33a pin the design sets to a constantzand reads (round 6: this said "inout read-back", but reading back a bidirectional pin works,33'sJA[0]and33b);51ca packed 2-D localparam whose type is a typedef (yosys builds the table as zeros),58a hierarchical name,59$isunknown,81a design module that calls$finish(the CLI takes it for a testbench and builds its submodule),93two roots, one named like its own file (the CLI builds that one, not the project's top); 63(int'of a real) was in the synth list until round 4 made it unverified.
- synth, yosys refuses:
- Silent wrongs, where the bitstream builds and the netlist fails:
03,03b,05,22c,22d,22f,23c,33,37b,39,51c,58,59,81,93(15). They are #3's, except23c, which #4's package work closes. Two more rows build a bitstream whose netlist fails,35and97, for lack of a model, not because they are known to be wrong; the runner counts all 17 alike (round 7: these notes and the plan said 14 and left35out).
Every probe. The 132 folders of test/sv, from the final run's rows (they equal expect.tsv). Per probe: the id, Vivado's status (supported, not supported, unverified), the verdict (ok: does what the table says and is not a known gap; gap: a known gap), the stages that fail, the runner's note if any, the construct, and where the Vivado status comes from. "Vivado 2020.1 log" is a Vivado log in a public CS223 (or CS-224) lab repo or in the course's example project, quoted in expect.tsv without the student's names; UG901 is the Vivado synthesis guide, UG953 the 7-series primitive guide. expect.tsv holds the full quote for every source and what the student sees today. Summary: 45 ok, 87 gap; 111 supported, 3 not supported, 18 unverified.
01_latch_comb supported gap fails: synth, bit (netlist not run)
always_comb with an if and no else (a latch is inferred)
source: Vivado 2020.1 log; UG901 v2023.2 p.84
02_unnamed_inst supported gap fails: rtl, synth, bit (netlist not run)
module instance with no instance name: inv(sw[0], led[0]);
source: Vivado 2020.1 log
03_async_reset_nonconst unverified gap fails: netlist
always_ff @(posedge clk or posedge btnC) if (btnC) q <= sw[3:0]; (async load from a signal)
source: UG901 v2023.2 p.81-82
03b_async_reset_selfref unverified gap fails: netlist
a button used as an async 'reset' that loads cnt+1 (the button-as-clock counter)
source: UG901 v2023.2 p.81-82
04_dual_edge unverified ok fails: synth, bit (netlist not run)
always @(posedge clk or negedge clk) cnt <= cnt + 1;
source: UG901 v2023.2 p.81; forum posts (weak source)
05_two_block_driver unverified gap fails: netlist
one reg written from two always blocks on different edges (posedge btnU / posedge btnC)
source: forum posts (weak source)
06_missing_end_default not supported ok fails: rtl, synth, bit (netlist not run)
a case item's begin (S2: begin) never closed before default: (invalid code; a typo seen in a
CS223 lab)
source: IEEE 1800-2017 A.6.7
07_enum_packed_array supported ok passes all four
packed array of enums: statetype [1:0] state, nextstate;
source: Vivado 2020.1 log; UG901 v2023.2 p.285
08_port_dir_inherit supported ok passes all four
ANSI port inheriting its direction: output [6:0] seg, logic dp, (the course's seven-segment
file)
source: Vivado 2020.1 log
08b_port_dir_comment supported gap fails: synth, bit (netlist not run)
the course's port line (output [6:0] seg, logic dp) with comments in the port list: one after
seg, one with a ';' in it
source: Vivado 2020.1 log; IEEE 1800-2017 5.4
08c_port_dir_inherit_proc supported gap fails: rtl
the course's port line shape with a range only, written in an always block: output logic [6:0]
seg, [3:0] an; with an set in an always_comb
source: Vivado 2021.2 log; IEEE 1800-2017 23.2.2.3; slang
09_always_procs supported ok passes all four
always_ff / always_comb / always_latch
source: UG901 v2023.2 p.286
10_enum_fsm supported ok passes all four
typedef enum FSM with an explicit base type and with the default (int), plus a light_t'(l+1)
cast
source: UG901 v2023.2 p.285
11_enum_methods supported gap fails: synth, bit (netlist not run)
enum methods s.first() / s.next() / s.last()
source: UG901 v2023.2 p.285
12_struct_packed supported ok passes all four
typedef struct packed, nested, member read and write
source: UG901 v2023.2 p.285
12b_struct_assign_pattern supported gap fails: rtl, synth, bit (netlist not run)
named assignment pattern '{hi: x, lo: y} and '{default: 0}
source: UG901 v2023.2 p.286
12c_struct_array_var_index supported gap fails: synth, bit (netlist not run)
a packed array of structs indexed by a signal, then a member select: cars[sw[15]].x
source: UG901 v2023.2 p.285, p.287
13_arrays_multidim supported ok passes all four
packed 2-D, unpacked, unpacked 2-D arrays, for (int i..) in always_comb
source: UG901 v2023.2 p.285
13b_unpacked_array_port supported gap fails: synth, bit (netlist not run)
an unpacked array as a module port: input logic [3:0] v [0:3]
source: UG901 v2023.2 p.161, p.162, p.164; UG901 v2022.2 p.160-162; IEEE 1800-2017 A.1.3
13c_unpacked_array_concat supported gap fails: rtl, synth, bit (netlist not run)
an unpacked array concatenation: assign arr = {sw[3:0], sw[7:4]}; to logic [3:0] arr [0:1]
source: UG901 v2023.2 p.286
13d_packed_2d_var_index supported gap fails: rtl
note: netlist: testbench only, iverilog cannot compile the RTL
a packed 2-D array indexed by a variable, then a bit or part select: grid[i][j] <= ... in a
loop, grid[sw[13:12]][3:1]
source: UG901 v2023.2 p.285; Vivado 2019.1 log
14_params supported ok passes all four
typed parameters, one depending on another, localparam, #(.W(8),.K(3)) and positional #(4)
source: UG901 v2023.2 p.288
15_generate supported ok passes all four
generate for with genvar (named block), for (genvar j..) with if-generate, case-generate
source: UG901 v2023.2 p.288
16_func_task supported ok passes all four
function automatic with a loop, plain function, task automatic called in always_comb
source: UG901 v2023.2 p.287
16b_func_return supported gap fails: synth, bit (netlist not run)
'return expr;' inside a function
source: UG901 v2023.2 p.287
16c_while_loop supported gap fails: synth, bit (netlist not run)
while loop in an always block (UG901 example)
source: UG901 v2023.2 p.257, p.258
17_clog2_bits_cast supported ok passes all four
$clog2 in a localparam and a range, $bits, size cast 4'(expr)
source: UG901 v2023.2 p.240, p.285
18_unique_priority supported ok passes all four
unique case / priority casez
source: UG901 v2023.2 p.287, p.279; UG901 v2022.2 p.290
18b_unique_priority_if supported gap fails: rtl
note: netlist: testbench only, iverilog cannot compile the RTL
unique if / priority if
source: UG901 v2023.2 p.287
18c_unique0_if supported gap fails: rtl
note: netlist: testbench only, iverilog cannot compile the RTL
unique0 if: an if/else if chain where no branch may match
source: UG901 v2023.2 p.287
19_casez_casex supported ok passes all four
casez with ? wildcards, casex
source: UG901 v2023.2 p.279
20_inside not supported ok fails: rtl, synth, bit (netlist not run)
set membership: x inside {1,3,[8:10]} and case (x) inside
source: UG901 v2023.2 p.287; UG901 v2022.2 Table 8-1
21_incdec_assignops supported ok passes all four
x++ and x += in always_comb, k++ inside always_ff, for (int i..;i++) with c++
source: UG901 v2023.2 p.286
21b_assign_in_expr supported gap fails: rtl
note: netlist: testbench only, iverilog cannot compile the RTL
an assignment within an expression: a = (b = sw[3:0]) + 4'd1; in always_comb
source: UG901 v2023.2 p.286
22_interface_modport supported gap fails: rtl
note: netlist: testbench only, iverilog cannot compile the RTL
interface with two modports, modport-typed module ports (sum_if.prod bus)
source: UG901 v2023.2 p.288, p.283-284
22b_interface_own_file supported gap fails: rtl, synth, bit (netlist not run)
an interface declared in its own file (sum_if.sv), generic interface ports
source: UG901 v2023.2 p.288
22c_interface_ports supported gap fails: netlist
an interface with ports, interface cnt_if(input logic clk, input logic en), holding a counter;
top reads c.q
source: UG901 v2023.2 p.288
22d_interface_array supported gap fails: netlist
an array of interfaces: pair_if p [0:1] (); p[0].d and p[1].d driven and read in top
source: UG901 v2023.2 p.288
22e_modport_expression supported gap fails: rtl, synth, bit (netlist not run)
a modport expression: modport lo (input .nib(data[3:0])); a submodule reads b.nib
source: UG901 v2023.2 p.288; IEEE 1800-2017 25.5.4; slang
22f_interface_member_access supported gap fails: netlist
an interface without ports whose members the module that instantiates it drives and reads:
add_if bus (); assign bus.a = sw[7:0]; ... led = bus.s
source: UG901 v2023.2 p.288, p.282
23_package_import supported gap fails: synth, bit (netlist not run)
package (enum, parameter, localparam, function) + import pkg::*; inside the module + pkg::NAME
source: UG901 v2023.2 p.288, p.284
23b_package_file_import supported gap fails: synth, bit (netlist not run)
the same package with import pkg::*; at file level, using an imported typedef
source: UG901 v2023.2 p.288, p.284
23c_package_own_file supported gap fails: rtl, netlist
a package in its own file (pkg.sv), referenced as cfg_pkg::NAME
source: UG901 v2023.2 p.288
23d_package_header_import supported gap fails: rtl, synth, bit (netlist not run)
import in the module header: module top import pkg::*; (...)
source: UG901 v2023.2 p.288
23e_package_export supported gap fails: synth, bit (netlist not run)
a package that exports a name it imported: package p2; import p1::K; export p1::K; endpackage,
and import p2::* in the module
source: UG901 v2023.2 p.288
24_preproc_include supported ok passes all four
`include of a .svh, `define with arguments, `ifdef/`ifndef/`else, +: part select
source: UG901 v2023.2 p.239
25_port_conn_dotstar supported ok passes all four
.* and .name implicit port connections
source: UG901 v2023.2 p.281-282, p.287
26_inst_array supported gap fails: synth, bit (netlist not run)
array of instances: inv u_inv [3:0] (.a(sw[3:0]), .y(led[3:0]));
source: UG901 v2023.2 p.239
27_readmemh_rom supported ok passes all four
ROM from initial $readmemh("rom.mem") with async and sync read
source: UG901 v2023.2 p.240, p.155, p.177
28_dist_ram supported ok passes all four
RAM: sync write, async read (distributed RAM, -nobram)
source: UG901 v2023.2 p.114
29_shift_reg_srl supported ok passes all four
32-deep shift register (SRL) + dynamic tap sr[sw[8:4]]
source: UG901 v2023.2 p.88, p.91
30_mult_signed supported ok passes all four
signed 8x8 multiply, >>> arithmetic shift, signed compare, $signed
source: UG901 v2023.2 p.94, p.286, p.240
31_reset_styles supported ok passes all four
sync active-high reset, async active-low reset (negedge rst_n), async set to a nonzero constant
source: UG901 v2023.2 p.81, p.82
31b_negedge_set supported ok passes all four
registers on the falling edge with no start value: a counter with a synchronous set (FDSE_1) and
a bit with an asynchronous preset (FDPE_1), both reset from two switches
source: UG901 v2023.2 p.81
32_clkdiv_7seg supported ok passes all four
clock divider making a derived clock (always_ff @(posedge slow)) + 4-digit 7-seg multiplexing +
hex ROM via case
source: Vivado 2020.1 log; UG901 v2023.2 p.81
33_tristate_pmod supported gap fails: netlist
inout JA: JA[0] = oe ? d : 1'bz and read back; JA[7:1] = 'z, JA[1] read as input
source: UG901 v2023.2 p.86; UG953 v2022.2 p.386
33b_inout_read_only supported ok passes all four
inout pin only read (never assigned 'z) + one bidirectional pin
source: UG901 v2023.2 p.86; UG953 v2022.2 p.386
33c_tristate_submodule_port supported gap fails: netlist, bit
the textbook's tristate buffer module (assign y = en ? a : 4'bz) instantiated four times, each
driving one nibble of an output tri [15:0] port
source: UG901 v2023.2 p.86
34_bufg supported gap fails: rtl
UNISIM primitive instantiation: BUFG
source: UG901 v2023.2 p.231-232; UG953 v2022.2 p.266
35_mmcm supported gap fails: rtl, netlist
UNISIM MMCME2_BASE (100 to 50 MHz) + BUFG
source: UG953 v2022.2 p.459; UG901 v2023.2 p.232
36_keep_attr supported ok passes all four
(* keep = "true" *) and (* dont_touch = "true" *) on internal nets
source: UG901 v2023.2 p.51, p.59
37_var_init unverified ok passes all four
register power-up value: logic [3:0] cnt = 5; and initial c2 = 9;
source: UG901 v2023.2 p.244, p.251, p.155
37b_decl_init_expr unverified gap fails: netlist
a variable declared with an initializer that reads inputs: logic [3:0] sum = sw[3:0] + sw[7:4];
(meant as an adder)
source: UG901 v2023.2 p.286, p.244; IEEE 1800-2017 6.8
38_gate_primitives supported gap fails: rtl
note: netlist: testbench only, iverilog cannot compile the RTL
gate primitives (not, and, or, xor) driving logic variables and an output logic port
source: Vivado 2015.2 log; UG901 v2023.2 p.242
39_undeclared_name unverified gap fails: rtl, netlist
a typo: an undeclared name on the right of an assign (summ for sum)
source: IEEE 1800-2017 6.10
40_display_module_name supported gap fails: rtl, synth, bit (netlist not run)
a module named display next to a $display call in a design module
source: IEEE 1800-2017 A.1.2; UG901 v2023.2 p.239
40b_module_name_in_string supported gap fails: rtl, synth, bit (netlist not run)
a module name and ( inside a $display string in a design module: $display("counter (u_cnt)
wrapped") next to module counter
source: UG901 v2023.2 p.239; IEEE 1800-2017 5.9
41_wildcard_equality supported gap fails: synth, bit (netlist not run)
wildcard equality: sw[7:0] ==? 8'b1??0_??01 and !=?
source: UG901 v2023.2 p.286
42_streaming_concat not supported ok fails: rtl, synth, bit (netlist not run)
streaming concatenation: {<<{sw[7:0]}} (bit reverse) and {<<4{sw[15:8]}}
source: UG901 v2023.2 p.287, p.286; UG901 v2022.2 p.289
43_foreach_loop supported gap fails: synth, bit (netlist not run)
foreach over an unpacked array in always_comb
source: UG901 v2023.2 p.287, p.280
44_do_while_loop supported gap fails: synth, bit (netlist not run)
do ... while in always_comb, twice: a popcount, and a loop whose condition is false on entry
(its body still runs once, which tells do-while from while)
source: UG901 v2023.2 p.287, p.280
45_break supported gap fails: synth, bit (netlist not run)
break out of a for loop in always_comb (lowest set bit)
source: UG901 v2023.2 p.280, p.287
46_continue unverified ok fails: synth, bit (netlist not run)
continue in a for loop in always_comb (count of set bits)
source: UG901 v2023.2 p.287
47_type_param supported gap fails: synth, bit (netlist not run)
type parameter: module inv_t #(parameter type T = logic [3:0]), overridden with #(.T(logic
[7:0]))
source: UG901 v2023.2 p.285
48_iff_event unverified ok fails: rtl, synth, bit (netlist not run)
iff event qualifier: always_ff @(posedge clk iff sw[15])
source: UG901 v2023.2 p.286; IEEE 1800-2017 9.4.2.3; slang; UG901 v2022.2 p.288
49_default_arg supported gap fails: synth, bit (netlist not run)
function argument with a default value, called without it
source: UG901 v2023.2 p.287
50_arg_by_name supported gap fails: synth, bit (netlist not run)
function arguments bound by name, in another order: diff(.b(x), .a(y))
source: UG901 v2023.2 p.287
51_unpacked_param_array supported gap fails: rtl, synth, bit (netlist not run)
an unpacked array localparam set with an assignment pattern: localparam logic [6:0] SEGS [0:3] =
'{...}
source: UG901 v2023.2 p.285, p.286
51b_packed_param_array supported gap fails: rtl, synth, bit (netlist not run)
a packed 2-D localparam as a lookup table: localparam logic [3:0][6:0] SEG = {...}; seg =
SEG[sw[1:0]];
source: UG901 v2023.2 p.285
51c_packed_param_typedef supported gap fails: rtl, netlist
the same lookup table with its type named first: typedef logic [3:0][6:0] tab_t; localparam
tab_t SEG = {...};
source: UG901 v2023.2 p.285
52_internal_tristate supported gap fails: netlist, bit
an internal tri bus with two tristate drivers taking turns (the textbook's mux from tristate
buffers)
source: UG901 v2023.2 p.86
53_async_reset_sync_clear supported gap fails: synth, bit (netlist not run)
async reset ORed with a synchronous clear: always_ff @(posedge clk, posedge btnC) if (btnC ||
btnU) q <= 0;
source: Vivado 2021.2 log
54_use_before_decl supported gap fails: rtl
note: netlist: testbench only, iverilog cannot compile the RTL
a name used before its declaration: assign led[0] = stop; logic stop;
source: Vivado 2017.3 log
55_enum_conditional unverified ok fails: rtl
note: netlist: testbench only, iverilog cannot compile the RTL
a conditional between two values of one enum: next = sw[0] ? RUN : IDLE;
source: UG901 v2023.2 p.285; slang
56_unit_scope_enum supported gap fails: synth, bit (netlist not run)
typedef enum {S0, S1, S2} outside the module (compilation-unit scope), with the textbook's state
names
source: UG901 v2023.2 p.270, p.285
56b_unit_typedef_file supported gap fails: rtl, synth, bit (netlist not run)
a typedef at file scope in a file of its own (types.sv holds only typedef enum ... state_t;),
used by the design's module
source: UG901 v2023.2 p.270
57_ready_module_netlist supported gap fails: rtl, synth, bit (netlist not run)
a course 'ready module' in netlist form in its own file ((* keep_hierarchy *), \<const0>, GND,
LUT2), instantiated by the design
source: Vivado 2017.3 log
58_hier_name supported gap fails: netlist
a hierarchical name that reads a submodule's signal: assign led[1:0] = u_fsm.state;
source: UG901 v2023.2 p.239, p.288
59_isunknown supported gap fails: netlist
$isunknown(sw) in a design (a real signal is never x, so it is 0)
source: UG901 v2023.2 p.240
60_pass_by_ref supported gap fails: rtl, synth, bit (netlist not run)
a function with a ref argument: function automatic void inc(ref logic [3:0] x)
source: UG901 v2023.2 p.287
61_enum_range supported gap fails: synth, bit (netlist not run)
enum ranges: typedef enum {S[3]} (S0, S1, S2) and {T[5:7]} (T5, T6, T7)
source: UG901 v2023.2 p.285
62_type_operator supported gap fails: rtl, synth, bit (netlist not run)
the type operator: var type(a) b;
source: UG901 v2023.2 p.285
63_real_cast unverified ok fails: synth, bit (netlist not run)
a real localparam cast to int: localparam int INC = int'(STEP); (2.6 rounds to 3)
source: UG901 v2023.2 p.285, p.273, p.286
63b_real_localparam_expr unverified ok fails: synth, bit (netlist not run)
a real literal in a localparam used in an expression: localparam MAX = 1e1; ... count == MAX - 1
(a lab writes 50e6)
source: UG901 v2023.2 p.285, p.273, p.286
64_unpacked_struct supported gap fails: rtl, synth, bit (netlist not run)
an unpacked struct: typedef struct { logic [3:0] a; logic [3:0] b; } (no packed)
source: UG901 v2023.2 p.285
64b_unpacked_union supported gap fails: rtl, synth, bit (netlist not run)
an unpacked union: typedef union { logic [7:0] byte_v; logic [3:0] nib; } (no packed), one
member written and read
source: UG901 v2023.2 p.286
65_stream_unpack supported gap fails: rtl, synth, bit (netlist not run)
streaming concatenation as the target of an assignment: {>>{a, b}} = sw[7:0];
source: UG901 v2023.2 p.287
66_alias unverified ok fails: rtl, synth, bit (netlist not run)
alias a = b; (two nets made one)
source: UG901 v2023.2 p.286; UG901 v2022.2 p.288-289
67_array_compare supported gap fails: rtl, synth, bit (netlist not run)
equality of two whole unpacked arrays: a == b, a != b
source: UG901 v2023.2 p.285, p.286
68_array_slice supported gap fails: rtl, synth, bit (netlist not run)
a slice of an unpacked array: b = a[2:3];
source: UG901 v2023.2 p.285
69_disable supported gap fails: synth, bit (netlist not run)
disable of a named block to leave a loop (the lowest switch that is up)
source: UG901 v2023.2 p.238
70_wor_wand supported gap fails: bit
a wor and a wand net, two drivers each
source: UG901 v2023.2 p.238
71_array_copy supported gap fails: rtl
note: netlist: testbench only, iverilog cannot compile the RTL
one whole unpacked array assigned to another in always_ff: b <= a;
source: UG901 v2023.2 p.285
72_void_func_outputs supported gap fails: rtl
note: netlist: testbench only, iverilog cannot compile the RTL
a void function that returns through output arguments
source: UG901 v2023.2 p.287
73_array_arg supported gap fails: rtl
note: netlist: testbench only, iverilog cannot compile the RTL
an unpacked array as a function argument
source: UG901 v2023.2 p.285
74_verilog2005_file supported gap fails: rtl, synth, bit (netlist not run)
a .v design file with bit and final as names (keywords in SystemVerilog, names in Verilog 2005)
source: UG901 v2023.2 p.269
75_timing_not_met supported gap fails: bit
a design that misses 100 MHz: a 14-bit value turned into four registered decimal digits (a long
divide chain)
source: Vivado 2015.2 log
76_implicit_net supported ok passes all four
names the standard declares implicitly: a net from a port connection (.y(w)) and one from the
left of an assign (assign eq = ...)
source: Vivado 2021.2 log; IEEE 1800-2017 6.10
77_initial_from_signal unverified ok fails: synth, bit (netlist not run)
an initial block that reads a signal: initial num = sw; (seen in a CS223 lab)
source: UG901 v2023.2 p.251, p.244
78_keep_hierarchy supported gap fails: rtl, synth, bit (netlist not run)
(* keep_hierarchy = "yes" *) on a module, spaced as in UG901
source: UG901 v2023.2 p.60
79_keep_hierarchy_nospace supported ok passes all four
the same attribute written without spaces: (*keep_hierarchy="yes"*) (yosys keeps the submodule)
source: UG901 v2023.2 p.60
80_async_reset_trailing unverified ok fails: synth, bit (netlist not run)
an always_ff with an async reset and a statement after its if/else (d <= q; runs on the reset
edge too)
source: UG901 v2023.2 p.81-82
81_finish_in_design supported gap fails: rtl, netlist
note: the CLI took counter for the top
a design module that calls $finish in a simulation check (always @(posedge clk) if (...)
$finish;), its submodule's ports named as the XDC's (counter.sv)
source: UG901 v2023.2 p.239, p.240
82_file_tasks_in_design supported gap fails: synth, bit (netlist not run)
file tasks left in a design module: $fopen, $fdisplay and $fwrite (a debug trace)
source: UG901 v2023.2 p.239
83_realtobits supported gap fails: rtl, synth, bit (netlist not run)
$realtobits and $bitstoreal in localparams (a real turned into its IEEE 754 bits and back)
source: UG901 v2023.2 p.240
84_repeat_const supported ok passes all four
a repeat loop with a constant count in always_comb (count the switches that are up)
source: UG901 v2023.2 p.238
85_bitstream_cast supported gap fails: rtl, synth, bit (netlist not run)
a bitstream cast to an unpacked array: q = quad_t'(sw[7:0]); with typedef logic [3:0] quad_t
[0:1]
source: UG901 v2023.2 p.285
86_extern_module supported gap fails: rtl, synth, bit (netlist not run)
an extern module declaration ahead of the module: extern module inv4(input logic [3:0] a, output
logic [3:0] y);
source: UG901 v2023.2 p.287
87_config supported gap fails: synth, bit (netlist not run)
a config declaration next to the design: config cfg; design work.top; default liblist work;
endconfig
source: UG901 v2023.2 p.239, p.288
88_class_static supported gap fails: rtl, synth, bit (netlist not run)
a class used for its static function, called as util::rev(sw[3:0]) (no object is made)
source: UG901 v2023.2 p.288
89_clkdiv_slow_logic supported gap fails: bit
a clock divider's output clocks more logic than 100 MHz allows (four decimal digits of a 14-bit
value on the slow clock); the XDC has no create_clock, as the course's master XDC arrives
source: Vivado 2015.2 to 2021.2 routed timing reports (11 lab runs)
90_elab_warning_initial supported gap fails: synth, bit (netlist not run)
a parameter check in an initial block: initial if (N < 8) $warning(...)
source: UG901 v2023.2 p.286, p.241
91_submodule_own_file supported ok passes all four
a design split over two files: top in design.sv, its submodule counter in counter.sv (the usual
lab layout)
source: UG901 v2023.2 p.270
92_display_in_design supported ok passes all four
a $display debug print left in a clocked design module: if (q == 4'd15) $display("wrapped");
source: UG901 v2023.2 p.239
93_two_roots_file_name supported gap fails: netlist
note: the CLI took counter for the top
two modules nothing instantiates, in one file named after one of them (counter.sv: the plain
counter, and the board version the project names as its top)
source: Vivado 2015.2 logs of two CS223 Lab 4 projects (.xpr and runme.log)
94_power_var_base supported gap fails: bit
a variable raised to a constant power: assign led = sw[7:0] ** 2;
source: UG901 v2023.2 p.278, p.249
95_empty_submodule unverified ok fails: netlist, bit
a submodule declared with its ports and no body (a lab part not written yet), its output used
source: none (no Vivado log; UG901 is silent)
96_port_one_bit_vector supported gap fails: bit
a port declared [N-1:0] with N = 1: a vector of one bit, numbered 0
source: UG901 v2023.2 p.285; IEEE 1800-2017 7.4
96b_port_offset_range supported gap fails: bit
ports numbered from 1: input logic [4:1] sw, with the lab's own XDC (sw[1] to sw[4] only)
source: UG901 v2023.2 p.285; IEEE 1800-2017 7.4
97_ram_style_block supported gap fails: netlist
UG901's RAM_STYLE attribute: (* ram_style = "block" *) on a 256-byte memory
source: UG901 v2023.2 p.64
98_module_named_primitive unverified ok fails: synth, bit (netlist not run)
a student's module named like a Xilinx primitive: module INV (GND, VCC and MUXF7 too)
source: UG901 v2023.2 p.232 (the guide is silent on the name)
99_string_parameter unverified ok fails: synth, bit (netlist not run)
a parameter of type string: parameter string INIT_FILE = "digits.mem" (a ROM's $readmemh file)
source: UG901 v2023.2 p.285, p.238, p.247, p.232
Does the suite catch breakage? Each mutation below was made in a scratch copy and reverted.
One bit of one LUT INIT flipped in the product's own netlists. Round 1's run drew 12 flips per probe with replacement (228 draws, some the same flip twice; its survivor list added up to 23, not the 24 it said). Round 2 drew distinct flips, 12 per probe or all a probe has, over the 30 probes whose netlist passes: 236 flips. The testbenches alone catch 101; round 1's
eqv.pycatches 171 and the new one the same 171; testbench andeqv.pytogether catch 209. Of the 27 left:- 6 are
55's, which has noeqv.pyrun (iverilog cannot compile its RTL, so only its testbench checks the netlist); - 11 are
10's, in LUTs that read the upper bits of its 32-bitintlight (l[27:24],l[11:8], ...), which only ever holds 0 to 2; - 5 sit in combinational probes where the bench already compared every input:
16's 1 and24's 2 give the same outputs on all 65536 inputs, and36's 2 are in the LUT that drives the kept netmid, which no output reads; - 5 are in clocked probes (
07's 1,09's 1,21's 2,29's 1) and also pass a 65536-cycle run: LUT input combinations the design never reaches, read from the netlist, not proved.
The new bench does not catch more of this sample; what it adds is shown by
eqv_check.shand by the breakers' flips (21atsw = ffffand at7fff), which the old one passed. Both are in the evidence below.Round 3 drew again, from
top.json, 12 distinct flips per probe or all a probe has, over the 37 probes whose netlist passes (25 of them have LUTs; this said 22 until round 4): 284 flips. The testbenches alone catch 154; round 2's bench catches 199 and round 3's the same 199, so starting the netlist as the board does and countingxlost nothing here; together 253. Of the 31 left: 10 are10's (itsint, as above), 407's, 109's, 216's, 224's and 236's (as above: LUT input combinations the design never reaches, or a net no output reads), 455's (all among the 11 of its 48 flips that do not change what its netlist does, see the break round), and 675's.75's testbench then checked 104 of the 16384 values; it now checks every one, and on 60 other flips of75it catches 48 where the old one caught 41. The 12 it passes cannot change a digit, because every value is compared.- 6 are
Source mutations of the 43 designs (one operator at a time, iverilog and the testbench): 113 killed, 42 survived before; 122 killed, 32 survived after. The 32 left are listed under the break round.
--fix-portsturned off:08fails,Module port `\dp' is neither input nor output.iverilog -g2005in the sim rule (round 1, the 51): 37 of 51 probes fail. 36 fail at rtl: every probe whose rtl passes except16c, which is Verilog-2005 apart fromlogicand'0, and iverilog 13 accepts those under-g2005with a warning.22fails because its rtl error moves fromdesign.sv:7todesign.sv:1.Every LUT's INIT set to 0 after synthesis (
setparam -set INIT 0 t:LUT*), so the bitstream still builds (round 1, the 51): 19 probes fail, all at the netlist stage. The 9 other probes whose netlist passes have no LUT cells (INV, CARRY4, flip-flops, RAM, wiring).The runner itself: the five checks above. Forcing the runner's
exact=0(the breaker's mutation) turns the unexpected-pass check red.Round 2, 15 mutations of the runner,
test/run.sh's row check andeqv.py, each on a copy of the fixed tree: every one turns at least one check red (list in the evidence below). Before round 2, each of the breakers' mutations here left the whole suite green.Round 3, 16 mutations of
eqv.pyand 4 of the runner, its checks and the port-line note, each on a copy: every one turns at least one check red (list in the evidence below). Before round 3, three of the breakers' netlist-stage mutations were caught by one probe, and only with the measured yosys (the comparison deleted:03; the testbench'sPASSnot required:39; no button presses:03), and four left the whole suite green (no exhaustive walk, no mostly-zeros values, azcounted as unknown, the port-line note one line down).Round 4, 17 mutations of
eqv.py, each on a copy, against the 30 cases: every one turns at least one red (list in the evidence below). Before round 4, the breakers'eqv.pymutations (buttons out of the walk, pins never driven, pins not compared, 512 cycles) and their runner and check mutations (the score counted wrong, the latch advice replaced,.vdropped from thefile:linepattern, the backquote dropped from the student-name pattern) each left the whole suite green. On the product's own netlists, a reset tied off in09,10,21,31,32or34: EQV PASS on round 3's bench, EQV MISMATCH now (each probe's testbench caught it already).Round 5, 8 mutations of
eqv.pyandeqv.sh, each on a copy, against the 37 cases, and 2 of the runner against its checks: every one turns at least one red (list in the evidence below). Before round 5, each of the breakers' mutations left the whole suite green: no compare after the buttons change, async-reset flip-flops or latches not started, a reset value read in the other bit order,flattendropped from the runner's register listing, the runner's"..."quote check and its vivado value check removed.Round 6, 12 mutations of
eqv.pyandeqv.shagainst the 48 cases, 5 of the runner andtest/run.shagainst their checks, and 3 of the product or a probe against the probes: every one turns at least one red (list in the evidence below). Before round 6, each of the breakers' ones left the whole suite green: no start for a multi-bit enum or a memory, no data compare with a clock,eqv.sh's bench guard, theassigned_infallback, the packages-first order, the "nothing compared" fail, the runner's second quote, secondfile:lineand exit-2 test,sv_rowsalways lenient, the name check cut tomodule,simcompiling one design file,$printkept for place and route,44read as a while loop.Round 7, 8 mutations of
eqv.pyagainst the 54 cases and 3 of the runner andtest/run.shagainst their checks: every one turns at least one red (list in the evidence below). Before round 7, the breakers' ones each left the whole suite green: thextest dropped from the sim-start excuse,STARTS_AT_1withoutFDSE_1andFDPE_1, the rtl stage's exit code not required, the score line not required, a home-folder path intest/run.sh.13d's LUT INIT flips: 414 of 432 caught (308 with the old testbench, the breaker's count), and the 18 left change nothing (above).Not caught: every LUT INIT stripped from the FASM before
fasm2frames(the breaker's mutation): the bitstream loses its logic, and only the golden blink FASM, which is written before that step, is compared. See "Tried, and dropped".
Evidence.
$ test/sv/run.sh
test/sv: 51 probes, yosys 0.69+post, iverilog 13.0
gap 01_latch_comb rtl pass synth fail netlist - bit fail
gap 03_async_reset_nonconst rtl pass synth pass netlist fail bit pass
ok 07_enum_packed_array rtl pass synth pass netlist pass bit pass
gap 18b_unique_priority_if rtl fail synth pass netlist pass bit pass note: netlist: testbench only, iverilog cannot compile the RTL
...
synthesis 35/51, all four stages 24/51, Vivado-supported probes passing all four stages 23/42, known gaps 23
matches expect.tsv: 51/51 (226 s)
$ PATH=<yowasp yosys 0.68 shim>:$PATH test/sv/run.sh
test/sv: 51 probes, yosys 0.68, iverilog 13.0 (expect.tsv was measured with yosys 0.69+post, iverilog 13.0: only regressions and new silent wrongs fail)
synthesis 35/51, all four stages 24/51, Vivado-supported probes passing all four stages 23/42, known gaps 23
matches expect.tsv: 51/51
$ SV_OUT=rows.tsv test/run.sh (from a scratch folder; rows.tsv is there afterwards, 2279 bytes)
== probe runner (test/sv/run.sh on a copy, one probe each)
PASS a build output left in a probe folder is not reused
PASS a new silent wrong fails with any yosys
PASS an unexpected pass fails with the measured yosys
PASS a row without a Vivado source is refused
PASS a wrong line in the latch error is caught
== SystemVerilog probes (test/sv)
GAP sv 01_latch_comb: rtl pass, synth fail, netlist -, bit fail
...
PASS sv 07_enum_packed_array: rtl pass, synth pass, netlist pass, bit pass
...
passed 86, failed 0, known gaps 23
The same new checks run against the tree before this round: the old personal-path grep misses the
planted line, student names FAIL, stale top_sim FAIL (the run said "rtl pass"), new silent wrong FAIL (the
run said "note: ..." and exit 0), empty source FAIL, latch line FAIL. The unexpected-pass check passes
there too: it guards the runner's exact mode, which was already right.
The breaker's LUT flip in 30's netlist (32'd2720070586 -> 32'd2686516154); the old testbench printed PASS:
tb on the netlist: FAIL: >>>, $signed or signed < (sw=1410 seg=0000010, want sh=1 a<b=1)
EQV FAIL: 128 of 1507328 compared output bits differ between the RTL and the netlist
Round 2:
$ test/run.sh (from a scratch folder, SV_OUT=rows-final.tsv)
== probe runner (test/sv/run.sh on a copy, one probe each)
PASS a testbench that prints FAIL is an rtl fail
PASS a probe folder without a row is refused
PASS a bit run without top.bit is a bit fail
PASS a vivado column its source does not back is refused
PASS a probe that stops matching expect.tsv fails this suite
PASS the verdict of each row is checked, not trusted
PASS eqv.py: an equal netlist passes
PASS eqv.py: a fault only on led[15] is caught
PASS eqv.py: a fault only at sw=0000 is caught
PASS eqv.py: a clocked fault only at sw=ffff is caught
PASS eqv.py: a clocked fault only at sw=7fff is caught
PASS eqv.py: a fault after 200 cycles is caught
== SystemVerilog probes (test/sv)
GAP sv 13b_unpacked_array_port: rtl pass, synth fail, netlist -, bit fail
GAP sv 52_internal_tristate: rtl pass, synth pass, netlist fail, bit fail
PASS sv 55_enum_conditional: rtl fail, synth pass, netlist pass, bit pass (note: netlist: testbench only, iverilog cannot compile the RTL)
GAP sv 57_ready_module_netlist: rtl fail, synth fail, netlist -, bit fail
...
synthesis 38/69, all four stages 24/69, Vivado-supported probes passing all four stages 23/60, known gaps 41
passed 99, failed 0, known gaps 41 (305 s)
$ PATH=<yowasp yosys 0.68 shim>:$PATH test/sv/run.sh
test/sv: 69 probes, yosys 0.68, iverilog 13.0 (expect.tsv was measured with yosys 0.69+post, ...)
synthesis 38/69, all four stages 24/69, Vivado-supported probes passing all four stages 23/60, known gaps 41
matches expect.tsv: 69/69 (the row check on these rows: 28 passed, 0 failed, 41 gaps)
$ test/sv/eqv_check.sh <case>, with round 1's eqv.py and with mutations of the new one
round 1's eqv.py equal ok msb ok zero RED ones RED dense RED deep ok
compare bit 0 equal ok msb RED zero ok ones ok dense ok deep ok
16 cycles equal ok msb ok zero ok ones ok dense RED deep RED
no corner values equal ok msb ok zero ok ones RED dense RED deep ok
The breakers' flips, round 1's eqv.py then the new one:
sw=0000 only: EQV PASS: 1048576 output bits compared -> EQV MISMATCH ... led[0] rtl 1, netlist 0; inputs sw=0000
21 at ffff: EQV PASS: 262024 output bits compared -> EQV MISMATCH ... led[11] rtl 0, netlist 1; inputs btnC=0 sw=ffff
21 at 7fff: (passed 4096 cycles) -> EQV MISMATCH ... led[11] rtl 1, netlist 0; inputs btnC=0 sw=7fff
Mutations, each on a copy of the fixed tree, and the check that goes red:
runner writes ok for bad a probe that stops matching expect.tsv fails this suite
row check files bad as gap the verdict of each row is checked, not trusted
row check drops the stage compare the verdict of each row is checked, not trusted
row check ignores the exit code the verdict of each row is checked, not trusted
rtl without grep PASS a testbench that prints FAIL is an rtl fail
silent wrong not a gap sv 05_two_block_driver: the runner says ok, but ... make it gap
folder/row check removed a probe folder without a row is refused
synth read from the exit code sv 52_internal_tristate: synth fail, expected pass
bit without top.bit a bit run without top.bit is a bit fail
vivado column check removed a vivado column its source does not back is refused
student-name regex gutted student-name check sees a quoted name
eqv: bit 0 only / 16 cycles / walk from 1 / no corner values eqv.py: msb / deep, dense / zero / ones, dense
The fixed tree without a mutation: all 13 of those checks pass.
Before and after, on the tree the breakers had:
13b PASS sv 13b_unpacked_array_port -> GAP sv 13b_unpacked_array_port
runner ok-for-bad + 14 broken PASS sv 14_params, exit 0 -> FAIL sv 14_params: the runner says ok, but its stages ... differ from expect.tsv
silent wrong not a gap PASS sv 03_async_reset_nonconst -> FAIL sv 03_async_reset_nonconst: the runner says ok, but ... make it gap
Round 3:
$ GITHUB_STEP_SUMMARY=summary.md SV_OUT=rows-final.tsv test/run.sh (from a scratch folder)
== probe runner (test/sv/run.sh on a copy, one probe each)
PASS a netlist that differs from its RTL fails, with any yosys
PASS a testbench FAIL on the netlist fails, with any yosys
PASS the runner checks leave CI's step summary alone
...
PASS eqv.py: a fault only at sw=1234 is caught
PASS eqv.py: a clocked fault only at sw=0100 is caught
PASS eqv.py: a fault only while btnC is pressed is caught
PASS eqv.py: an undriven (z) output is caught
PASS eqv.py: an x output is caught
PASS eqv.py: a register with INIT x starts at 0, as on the board
PASS eqv.py: a start-value difference before the reset is apart
PASS eqv.py: a kept submodule named as the RTL's passes
PASS eqv.py: a fault inside a kept submodule is caught
== SystemVerilog probes (test/sv)
GAP sv 58_hier_name: rtl pass, synth pass, netlist fail, bit pass
GAP sv 59_isunknown: rtl pass, synth pass, netlist fail, bit pass
GAP sv 75_timing_not_met: rtl pass, synth pass, netlist pass, bit fail
PASS sv 79_keep_hierarchy_nospace: rtl pass, synth pass, netlist pass, bit pass
...
synthesis 47/92, all four stages 26/92, Vivado-supported probes passing all four stages 25/77, known gaps 56
passed 119, failed 0, known gaps 56 (409 s)
$ cat summary.md (one line; 8 before)
SystemVerilog probes (yosys 0.69+post, iverilog 13.0): synthesis 47/92, all four stages 26/92, ...
$ PATH=<yowasp yosys 0.68 shim>:$PATH test/sv/run.sh (CI's yosys; brew untouched)
test/sv: 92 probes, yosys 0.68, iverilog 13.0 (expect.tsv was measured with yosys 0.69+post, ...)
synthesis 47/92, all four stages 26/92, Vivado-supported probes passing all four stages 25/77, known gaps 56
matches expect.tsv: 92/92 (the only notes: 8 "testbench only")
$ PATH=<the same shim>:$PATH test/sv/eqv_check.sh <each of the 15 cases>: 15 of 15 as expected
eqv.py mutations, each on a copy, and the eqv_check.sh cases that go red:
exhaustive walk off (EXHAUSTIVE_BITS = 0) walk
no mostly-zeros values sparse
no mostly-ones values dense
buttons never pressed button
a z counted as unknown zout
an x counted as unknown xout
INITs left x (no board start) init, xstart
no start-value excuse xstart
every 0/1 difference excused msb, zero, walk, ones, dense, sparse, deep, button, hierfault
the excuse without its 0/1 test xout
only top renamed hier, hierfault
cell types not renamed hierfault
compare bit 0 only msb, zout, xout
16 cycles dense, sparse, deep
walk from 1 zero
no corner values ones, dense, sparse
The fixed eqv.py: all 15 cases pass.
The runner, and the checks that go red:
netlist-vs-RTL comparison deleted (CI mode) FAIL a netlist that differs from its RTL fails, with any yosys
testbench PASS dropped from the netlist stage FAIL a testbench FAIL on the netlist fails, with any yosys
the summary check on the round-2 tree FAIL the runner checks leave CI's step summary alone (PASS now)
the port-line note one line down ok 08 on the round-2 tree -> BAD 08_port_dir_inherit ... expect.tsv says design.sv:2, no stage printed it
Before and after:
79 on the round-2 tree BAD 79_keep_hierarchy_nospace ... net_eqv.v:3: error: 'inv' has already been declared in this scope. -> ok
the undriven led[4] EQV PASS: 983040 output bits compared, all equal (65536 more were x in the netlist only) -> EQV MISMATCH at 3000 (gray): led[4] rtl 0, netlist x; inputs sw=0000
55's 48 LUT flips the old testbench passes 23 of the 36 that change the netlist -> the new one passes 0
Round 4:
$ GITHUB_STEP_SUMMARY=<absolute path> SV_OUT=rows-final.tsv test/run.sh (from a scratch folder)
== static
PASS student-name check sees a quoted name
== probe runner (test/sv/run.sh on a copy, one probe each)
PASS the score line is counted again from the rows
PASS a message the today column quotes is checked
PASS a file:line of a .v file is checked
...
PASS eqv.py: a packed array of enums with no start value is started
PASS eqv.py: a 1-bit enum with no start value is started
PASS eqv.py: a netlist that lost its reset is caught
PASS eqv.py: a re-encoded state machine is compared after one reset
PASS eqv.py: ports named x, mode, seed, r, n, v, k do not clash with the bench
...
== SystemVerilog probes (test/sv)
PASS sv 07_enum_packed_array: rtl pass, synth pass, netlist pass, bit pass
GAP sv 13c_unpacked_array_concat: rtl fail, synth fail, netlist -, bit fail
GAP sv 18c_unique0_if: rtl fail, synth pass, netlist pass, bit pass (note: netlist: testbench only, iverilog cannot compile the RTL)
GAP sv 22c_interface_ports: rtl pass, synth pass, netlist fail, bit pass
GAP sv 22d_interface_array: rtl pass, synth pass, netlist fail, bit pass
PASS sv 63_real_cast: rtl pass, synth fail, netlist -, bit fail
GAP sv 81_finish_in_design: rtl fail, synth pass, netlist fail, bit pass (note: the CLI took counter for the top)
...
synthesis 52/100, all four stages 26/100, Vivado-supported probes passing all four stages 25/84, known gaps 63
passed 138, failed 0, known gaps 63 (556 s)
$ cat summary.md (GITHUB_STEP_SUMMARY=<absolute path>; one line)
SystemVerilog probes (yosys 0.69+post, iverilog 13.0): synthesis 52/100, all four stages 26/100, Vivado-supported probes passing all four stages 25/84, known gaps 63
$ PATH=<yowasp yosys 0.68 shim>:$PATH test/sv/run.sh <the 100, in four runs> (CI's yosys; brew untouched)
test/sv: 25 probes, yosys 0.68, iverilog 13.0 (expect.tsv was measured with yosys 0.69+post, ...)
matches expect.tsv: 25/25, 25/25, 25/25, 24/25 BAD 75_timing_not_met ... expect.tsv quotes 'ERROR: timing not met: 33.61 MHz (FAIL at 100.00 MHz)', no stage printed it
after the rewording, 75 under both: gap 75_timing_not_met rtl pass synth pass netlist pass bit fail
$ PATH=<the same shim>:$PATH test/sv/eqv_check.sh <each of the 30 cases>: 30 of 30 as expected
$ GITHUB_STEP_SUMMARY=<absolute path> test/sv/run.sh 07_enum_packed_array 81_finish_in_design; cat <that file>
SystemVerilog probes (yosys 0.69+post, iverilog 13.0): synthesis 2/2, all four stages 1/2, Vivado-supported probes passing all four stages 1/2, known gaps 1
$ test/sv/eqv_check.sh <case>, round 3's eqv.py (its argument count only widened) and today's
round 3 today
names eqv.sv:12: error: 'r' has already been declared in this scope. EQV PASS: 98304 output bits compared, all equal
lostreset EQV PASS: 262136 ... (31956 more differed only while a register had no start value) EQV FAIL: 33108 of 262144 ...
lostinit EQV PASS: 196608 ... (35328 more differed only while ...) EQV FAIL: 35328 of 196608 ...
(the other 27 cases give the same verdict with both; they pin what round 3 left unpinned, below)
eqv.py mutations, each on a copy, and the eqv_check.sh cases that go red:
no reset press before the first compare recode
every start value 0 fillset fillaset
no start values in the RTL xstart pkenum enum1
LDPE starts at 1 ldpe ldfill
FDSE starts at 0 fdse fillset
FDPE starts at 0 fillaset
a latch's start taken from its preset ldfill
buttons out of the exhaustive walk cbutton
inout pins never driven from outside ioread iofloat
inout pins not compared iofloat
512 cycles / 3999 cycles deep / deep
buttons never pressed after the start button
netlist INITs left x init fdse ldpe ldfill xstart fillset fillaset pkenum enum1
only top renamed hierfault
every register set bit by bit (07's crash) pkenum enum1
a 1-bit enum not started enum1
Today's eqv.py: all 30 cases as expected.
The product's netlists with every reset/set pin that is not a constant tied to 0 (tb.sv on it; round 3's bench; today's):
09 tb FAIL EQV PASS: 262132 output bits compared, all equal (31956 more ...) EQV MISMATCH at 28000 (data): led[0] rtl 0, netlist 1
10 tb FAIL EQV PASS: 262140 ... (20140 more ...) EQV MISMATCH at 28000 (data): led[4] rtl 0, netlist 1
21 tb FAIL EQV PASS: 262120 ... (96886 more ...) EQV MISMATCH at 28000 (data): led[0] rtl 0, netlist 1
31 tb FAIL EQV PASS: 262092 ... (96814 more ...) EQV MISMATCH at 28000 (data): led[0] rtl 0, netlist 1
32 tb FAIL EQV PASS: 196608 ... (46705 more ...) EQV MISMATCH at 36000 (posedg): an[0] rtl 0, netlist 1
34 tb FAIL EQV PASS: 196604 ... (24440 more ...) EQV MISMATCH at 14000 (btns): led[0] rtl 0, netlist 1
The runner and test/run.sh's row check over 07, 01, 52, 70, each with a mutated runner:
round 3's tree, supported probes all counted as passing passed 1, failed 0 (the line says 4/4)
today, the same FAIL sv: test/sv/run.sh says '... passing all four stages 4/4, known gaps 3', its rows make it '... 1/4, known gaps 3'
today, synthesis counted from the bit stage FAIL sv: test/sv/run.sh says 'synthesis 1/4, ...', its rows make it 'synthesis 3/4, ...'
today, no mutation passed 1, failed 0, known gaps 3
The latch advice replaced with 'something went wrong.':
round 3's runner gap 01_latch_comb rtl pass synth fail netlist - bit fail matches expect.tsv: 1/1
today's runner BAD 01_latch_comb ... expect.tsv quotes 'Give q a default value at the top of the block.', no stage printed it
Each mutation on a copy, the new check that goes red (all four green on round 3's suite):
the backquote dropped from the student-name pattern FAIL student-name check sees a quoted name
.v dropped from the runner's file:line pattern FAIL a file:line of a .v file is checked
the runner's quote check removed FAIL a message the today column quotes is checked
the runner counts every supported probe FAIL sv: test/sv/run.sh says ... (the full run's row check, above)
81 with a runner that reads only top.json (round 3's), and today's:
BAD 81_finish_in_design rtl fail synth fail netlist - bit fail synth fail, expected pass; netlist -, expected fail; bit fail, expected pass
gap 81_finish_in_design rtl fail synth pass netlist fail bit pass note: the CLI took counter for the top
Round 5:
$ GITHUB_STEP_SUMMARY=<absolute path> SV_OUT=rows-final.tsv test/run.sh (from a scratch folder)
== static
PASS shellcheck (now also test/sv/eqv.sh)
== error paths
PASS unnamed instance -> line and fix (now also 'Write: sub u_sub(')
== probe runner (test/sv/run.sh on a copy, one probe each)
PASS a vivado value other than supported, not supported, unverified is refused
PASS a message the today column quotes in double quotes is checked
PASS eqv.py: an output lost only between a button change and the clock edge is caught
PASS eqv.py: a register with an asynchronous reset starts in the RTL as on the board
PASS eqv.py: a latch with no start value starts at 0 in the RTL, as on the board
PASS eqv.py: a reset value of 0011 starts each bit as its FDSE or FDRE does
PASS eqv.sh: a submodule's register with no start value is started in the RTL
PASS eqv.sh: a fault in a submodule's register with no start value is caught
PASS eqv.py: a register that feeds a submodule's port is started by its own name
== SystemVerilog probes (test/sv)
GAP sv 08c_port_dir_inherit_proc: rtl fail, synth pass, netlist pass, bit pass
GAP sv 51c_packed_param_typedef: rtl fail, synth pass, netlist fail, bit pass
PASS sv 84_repeat_const: rtl pass, synth pass, netlist pass, bit pass
...
synthesis 57/115, all four stages 27/115, Vivado-supported probes passing all four stages 26/99, known gaps 77
passed 148, failed 0, known gaps 77 (474 s)
$ cat summary.md (one line)
SystemVerilog probes (yosys 0.69+post, iverilog 13.0): synthesis 57/115, all four stages 27/115, Vivado-supported probes passing all four stages 26/99, known gaps 77
$ PATH=<yowasp yosys 0.68 shim>:$PATH test/sv/run.sh <the 15 new probes> (CI's yosys; brew untouched)
test/sv: 15 probes, yosys 0.68, iverilog 13.0 (expect.tsv was measured with yosys 0.69+post, ...)
matches expect.tsv: 15/15 (52 s)
$ PATH=<the same shim>:$PATH test/sv/eqv_check.sh <each of the 37 cases>: 37 of 37 as expected (111 s)
$ test/sv/run.sh <the 115, in four runs of 29, 29, 29 and 28>
matches expect.tsv: 29/29, 29/29, 29/29, 28/28 (124 s, 105 s, 51 s, 55 s)
The breaker's probe 90_reg_to_port (register n feeds a submodule's port D; top also has a register D):
round 4's runner BAD 90_reg_to_port rtl pass synth pass netlist fail bit pass netlist fail, expected pass
net: eqv.sv:20: error: 'eqv_rtl.A1.D' is not a valid l-value for a procedural assignment.
today's runner ok 90_reg_to_port rtl pass synth pass netlist pass bit pass
The breaker's divider in a submodule, the product's netlist of a copy that counts down:
round 4's runner, its eqv() as it is EQV FAIL: 26112 of 196608 compared output bits differ between the RTL and the netlist
round 4's runner, its eqv() without flatten EQV PASS: 147456 output bits compared, all equal
today's eqv.sh EQV FAIL: 26112 of 196608 compared output bits differ between the RTL and the netlist
eqv mutations, each on a copy; the cases that go red. Round 4's tree ran its 30 cases plus the breakers' 4
(mealyb, xstarta, xstartl, fillasym added to it); today's tree runs its 37:
round 4's tree today
no compare after the buttons change (eqv.py) mealyb (the 30: none) mealyb
async-reset flip-flops not started (eqv.py) xstarta (the 30: none) xstarta
latches not started (eqv.py) xstartl (the 30: none) xstartl
reset value bits in the other order (eqv.py) fillasym (the 30: none) fillasym
no flatten in the RTL register listing none (it was in run.sh) subfault (it is in eqv.sh)
no proc in the RTL register listing (eqv.sh) 25 cases, subreg subfault regport among them
the always block's module not looked at first subreg subfault regport
eqv.py --netlist skipped (INITs left x, modules not renamed) 17 cases
Today's tree without a mutation: all 37 cases as expected (21 s).
The runner checks on a copy of today's tree with the breakers' runner mutations:
"..." quotes not read from the today column FAIL st_fixtext (the run: gap 02_unnamed_inst ... matches expect.tsv: 1/1)
the vivado value check removed FAIL st_vivvalue (the run: ok 01_latch_comb ... matches expect.tsv: 1/1)
On round 4's tree the same two mutations left the suite green: 02 'matches expect.tsv: 1/1' with the fix
text gone from the CLI, 01 'ok' with its column written 'Supported'.
The new probes' testbenches, each on a correct design and on one with its construct broken:
08c the line bit rewrites, iverilog PASS an = 4'b1110 for two digits FAIL: sw=0100 an=1110 seg=1111111
12c iverilog PASS .x and .y swapped FAIL: sw=0001 led=0001
13d yosys-slang netlist PASS sw[4*j + i] in the loop FAIL: sw=0101 led=5005 want=1101
13d yosys-slang netlist PASS the two read indices swapped FAIL: sw=1010 led=0010 want=1010
23e iverilog / yosys-slang PASS K = 6 FAIL: sw=0000 led=0006 (both)
33c iverilog PASS 4'b0 instead of 4'bz FAIL: sw=0000 led=0000
40b iverilog PASS the counter steps by 2 FAIL: after 1 edges led=a5a2
51b yosys-slang netlist PASS two table entries swapped FAIL: sw=0002 seg=0110000
51c yosys-slang netlist PASS two table entries swapped FAIL: sw=0002 seg=0110000
82 iverilog PASS q + sw[4:1] FAIL: cycle 0 led=0002 want=4
83 yosys-slang netlist PASS $realtobits(3.5) FAIL: sw=0000 led=400a
83 yosys-slang netlist PASS $bitstoreal of 1.0 FAIL: sw=0000 led=4000
84 iverilog PASS repeat (7) FAIL: sw=da80 led=da00
85 yosys-slang netlist PASS quad_t [1:0] instead of [0:1] FAIL: sw=c201 led=c201
86 yosys-slang netlist PASS y = a FAIL: sw=0000 led=0000
87 iverilog PASS the byte swap undone FAIL: sw=3c00 led=3c00
88 yosys-slang netlist PASS rev returns a FAIL: sw=0101 led=0101
51c, the product's netlist and yosys-slang's, sw = 0 to 3 (the table: 1000000 1111001 0100100 0110000):
dewfpga bit pnr ok: 0 LUT, 0 FF ... seg 0000000 0000000 0000000 0000000
yosys-slang seg 1000000 1111001 0100100 0110000
Round 6:
$ GITHUB_STEP_SUMMARY=<absolute path> SV_OUT=rows-final.tsv test/run.sh (from a scratch folder)
== static
PASS student-name check sees a quoted name (now a quoted name after each of 10 words)
== probe runner (test/sv/run.sh on a copy, one probe each)
PASS an unexpected pass in a row is not trusted with the measured yosys
PASS a message the today column quotes second is checked
PASS a file:line the today column names second is checked
PASS a comparison that fails with exit 1 is a netlist fail
PASS eqv.py: a register whose slice a wire names is started by its own name
PASS eqv.py: a register with an alias wire that sorts first is started by its own name
PASS eqv.py: 1-bit enums of a submodule and of the file are started
PASS eqv.py: a 2-bit enum the netlist starts in a state the RTL never has is caught
PASS eqv.py: a LUT RAM that powers up with the wrong contents is caught
PASS eqv.py: an output lost only between a switch change and the clock edge is caught
PASS eqv.sh: a bench that does not compile while the RTL does is a FAIL
PASS eqv.sh: a package in its own file that sorts after the design is compiled first
PASS eqv.py: a bench that compares nothing is a FAIL
PASS eqv.py: a netlist one clock off at the start, as dewfpga sim shows it, passes
PASS eqv.py: a netlist that mixes two starts in one compare is caught
== SystemVerilog probes (test/sv)
GAP sv 22f_interface_member_access: rtl pass, synth pass, netlist fail, bit pass
GAP sv 37b_decl_init_expr: rtl pass, synth pass, netlist fail, bit pass
GAP sv 44_do_while_loop: rtl pass, synth fail, netlist -, bit fail
GAP sv 56b_unit_typedef_file: rtl fail, synth fail, netlist -, bit fail
PASS sv 63b_real_localparam_expr: rtl pass, synth fail, netlist -, bit fail
GAP sv 89_clkdiv_slow_logic: rtl pass, synth pass, netlist pass, bit fail
GAP sv 90_elab_warning_initial: rtl pass, synth fail, netlist -, bit fail
PASS sv 91_submodule_own_file: rtl pass, synth pass, netlist pass, bit pass
PASS sv 92_display_in_design: rtl pass, synth pass, netlist pass, bit pass
...
synthesis 62/123, all four stages 29/123, Vivado-supported probes passing all four stages 28/105, known gaps 82
passed 166, failed 0, known gaps 82 (533 s; rows-final.tsv has 123 rows)
$ cat summary.md (one line)
SystemVerilog probes (yosys 0.69+post, iverilog 13.0): synthesis 62/123, all four stages 29/123, Vivado-supported probes passing all four stages 28/105, known gaps 82
$ test/sv/run.sh <the 123, in four runs of 31, 31, 31 and 30>
matches expect.tsv: 31/31, 31/31, 31/31, 30/30 (136 s, 114 s, 39 s, 73 s)
synthesis 19+23+8+12 = 62/123, all four stages 12+12+0+5 = 29/123, supported and all four 28/105, known gaps 82
$ PATH=<yowasp yosys 0.68 shim>:$PATH test/sv/run.sh <the 8 new probes and the 6 changed rows> (CI's yosys)
test/sv: 14 probes, yosys 0.68, iverilog 13.0 (expect.tsv was measured with yosys 0.69+post, ...)
matches expect.tsv: 14/14 (63b's crash quote removed first: 13/14 with it)
$ PATH=<the same shim>:$PATH test/sv/eqv_check.sh <each of the 48 cases>: 48 of 48 as expected (152 s)
The breakers' netlists, round 5's eqv.sh, then today's:
Gray counter (6 FDRE) EQV FAIL: 9 of 36864 compared output bits differ between the RTL and the netlist
EQV PASS: 36864 output bits compared, all equal (at 9 compares to the RTL as dewfpga sim starts it, ...)
enum in a submodule EQV FAIL: the RTL compiles but the equivalence bench does not (Unable to bind `eqv_rtl.OFF')
EQV PASS: 196608 output bits compared, all equal
wire low = led[3:0] EQV FAIL: the RTL compiles but the equivalence bench does not ('eqv_rtl.low' is not a valid l-value)
EQV PASS: 262144 output bits compared, all equal
The breakers' netlists that have to fail, today's bench (the excuse passes none of them):
enum state 11 at power-up EQV FAIL: 2 of 196608 compared output bits differ between the RTL and the netlist
28's RAM all ones EQV FAIL: 4096 of 262144 compared output bits differ between the RTL and the netlist
led[0] a constant 1 EQV FAIL: 3609 of 196608 compared output bits differ between the RTL and the netlist
The new eqv_check cases on round 5's eqv.py and eqv.sh (red = the case does not give its verdict):
regslice alias enum1sub romstart red (the first three: the bench does not compile;
romstart: EQV FAIL: 21501 of 196608)
enumstart memstart datasw nobench pkgfile nothing mixstart as expected (they pin what round 5 already did)
eqv mutations on a copy of today's tree, and the cases that go red (48 cases; none without a mutation):
12 the multi-bit enum start deleted enumstart
13 memories not started memstart
14 no data compare with a clock enumstart datasw
15 eqv.sh's bench guard removed nobench
18 the assigned_in fallback removed regslice alias enum1sub
22 the packages-first order removed pkgfile
23 "nothing compared" no longer a FAIL nothing
the sim-start excuse removed romstart
the excuse per bit, not per compare mixstart
an x in the sim-start copy excuses subfault enumstart memstart
the 1-bit enum item named in top's scope enum1sub
assigned_in reads whole lines, not columns alias enum1sub
On round 5's tree the breakers' seven (12 to 23) each left all 37 cases green.
Runner mutations on a copy of today's tree, the old checks that could see them, and the new one:
only a row's first quote is checked PASS st_latchtext, PASS st_fixtext, FAIL st_quote2
only a row's first file:line is checked PASS st_latchline, PASS st_vfile, FAIL st_line2
any eqv.sh failure is a skip (-ne 0) PASS st_eqvpin, FAIL st_eqvexit
sv_rows always lenient (exact=0) PASS st_verdicts, PASS st_score, FAIL st_exact
the name check's words cut to module FAIL st_names
Product and probe mutations, the probe that goes BAD:
sim compiles only the first design file BAD 91_submodule_own_file rtl fail, expected pass (tb.sv:7: error: Unknown module type: top)
delete t:$print dropped BAD 92_display_in_design bit fail, expected pass (no Bels remaining of type '$print')
44's two do-while loops written as while BAD 44_do_while_loop rtl fail, expected pass
Before, no probe could see the first two (only 57 has two design files, and the CLI stops it; 40, 40b
and 81 never hand their $display to yosys); the breakers' full runs with them: passed 144, failed 0.
44 with its one loop written as while matched expect.tsv (gap, 1/1).
-noautowire, every probe read with and without it (hierarchy, proc): 22, 22b, 22c, 22d, 22f, 23c, 39, 58, 76
change; 63b crashes either way.
The new probes' testbenches, on the RTL (iverilog) and with one edit:
89 PASS d1 without % 10 FAIL: value 9999: led=9979, want 9999
89 PASS the slow clock never toggles FAIL: value 9999: led=0000, want 9999
90 PASS N = 5 FAIL: sw=0010 led=0010, want 0000
63b PASS MAX = 1.2e1 FAIL: after 10 edges led=0000
37b PASS wire instead of logic (continuous) FAIL: led follows sw (sw=0101 led=0001, was 0000): an initializer runs once
22f PASS s = a - b FAIL: sw=0100 led=00ff
91 PASS the counter steps by 2 FAIL: after 1 edges sw=5e81 led=5e83
92 PASS the counter steps by 2 FAIL: after 1 edges sw=5e81 led=5e82
56b PASS RUN goes back to IDLE FAIL: after 2 edges led=0000, want 2
44 PASS the second loop written as while FAIL: do-while popcount, and a body that runs once (sw=8000 led=0001 want 1, 1)
Round 6's final run, 26 September (the tree after round 6):
$ GITHUB_STEP_SUMMARY=<absolute path> SV_OUT=rows-final.tsv test/run.sh (from a scratch folder)
== static
PASS shellcheck
PASS no personal paths
PASS personal-path check sees test/sv/run.sh
PASS no names from student code in expect.tsv
PASS student-name check sees a quoted name
...
== probe runner (test/sv/run.sh on a copy, one probe each) 71 PASS: 23 runner checks, 48 eqv checks
...
== SystemVerilog probes (test/sv) 41 PASS, 82 GAP, 0 FAIL
GAP sv 01_latch_comb: rtl pass, synth fail, netlist -, bit fail
...
PASS sv 92_display_in_design: rtl pass, synth pass, netlist pass, bit pass
synthesis 62/123, all four stages 29/123, Vivado-supported probes passing all four stages 28/105, known gaps 82
passed 166, failed 0, known gaps 82
real 527,76 (exit 0; rows-final.tsv has 123 rows)
$ cat summary.md (one line)
SystemVerilog probes (yosys 0.69+post, iverilog 13.0): synthesis 62/123, all four stages 29/123, Vivado-supported probes passing all four stages 28/105, known gaps 82
$ PATH=<yowasp yosys 0.68 shim>:$PATH SV_OUT=<chunk>.tsv test/sv/run.sh <25 ids> (CI's yosys; brew untouched; 5 runs)
test/sv: 25 probes, yosys 0.68, iverilog 13.0 (expect.tsv was measured with yosys 0.69+post, iverilog 13.0: only regressions and new silent wrongs fail)
matches expect.tsv: 25/25, 25/25, 25/25, 25/25, 23/23 (175 s, 149 s, 129 s, 61 s, 131 s; 0 BAD)
synthesis 14+16+14+6+12 = 62/123, all four stages 9+8+7+0+5 = 29/123,
Vivado-supported all four 9+8+6+0+5 = 28 of 20+24+19+21+21 = 105, known gaps 14+16+15+21+16 = 82
notes: 11 "netlist: testbench only, iverilog cannot compile the RTL", 1 "the CLI took counter for the top"
$ diff <the native rows, id to bit> <the 0.68 rows, id to bit> (identical, 123 rows)
$ PATH=<the same shim>:$PATH test/sv/eqv_check.sh <each of the 48 cases>
as expected: 48 of 48; red: none (151 s)
$ readlink /opt/homebrew/bin/dewfpga; yosys -V
<the repo>/bin/dewfpga
Yosys 0.69+post (git sha1 143eb14f9cc55d6f8927e68523b0c9d2166ed02c, ...)
Round 7:
$ GITHUB_STEP_SUMMARY=<absolute path> SV_OUT=rows-final.tsv test/run.sh (from a scratch folder)
== static
PASS personal-path check sees test/run.sh (new)
== probe runner (test/sv/run.sh on a copy, one probe each)
PASS a testbench that prints PASS while dewfpga sim exits 1 is an rtl fail (new)
PASS the score line is counted again from the rows (now also: no score line, 'Synthesis ...')
PASS eqv.py: a wrong netlist that matches sim's x-optimistic copy at some compares is caught
PASS eqv.py: a netlist that shows only what sim shows while its register is x is caught
PASS eqv.py: an x in sim's copy excuses no x in the netlist
PASS eqv.py: a reset on a switch is pressed first, like a button
PASS eqv.sh: a register that shares its variable with an assign is started
PASS eqv.py: an FDSE_1 and an FDPE_1 with INIT x start at 1, as on the board
== SystemVerilog probes (test/sv)
PASS sv 31b_negedge_set: rtl pass, synth pass, netlist pass, bit pass
GAP sv 93_two_roots_file_name: rtl pass, synth pass, netlist fail, bit pass (note: the CLI took counter for the top)
GAP sv 94_power_var_base: rtl pass, synth pass, netlist pass, bit fail
PASS sv 95_empty_submodule: rtl pass, synth pass, netlist fail, bit fail
GAP sv 96_port_one_bit_vector: rtl pass, synth pass, netlist pass, bit fail
GAP sv 96b_port_offset_range: rtl pass, synth pass, netlist pass, bit fail
GAP sv 97_ram_style_block: rtl pass, synth pass, netlist fail, bit pass
PASS sv 98_module_named_primitive: rtl pass, synth fail, netlist -, bit fail
PASS sv 99_string_parameter: rtl pass, synth fail, netlist -, bit fail
...
synthesis 69/132, all four stages 30/132, Vivado-supported probes passing all four stages 29/111, known gaps 87
passed 178, failed 0, known gaps 87 (583 s; exit 0; rows-final.tsv has 132 rows, 178 PASS, 87 GAP, 0 FAIL)
final run, after every file but these notes was written: the same, 582 s, the same 132 rows
$ cat summary.md (one line)
SystemVerilog probes (yosys 0.69+post, iverilog 13.0): synthesis 69/132, all four stages 30/132, Vivado-supported probes passing all four stages 29/111, known gaps 87
$ SV_OUT=<chunk>.tsv test/sv/run.sh <the 132, in five runs of 27, 27, 27, 27 and 24>
matches expect.tsv: 27/27, 27/27, 27/27, 27/27, 24/24 (the first 118 s)
synthesis 15+19+13+10+12 = 69/132, all four stages 10+11+4+1+4 = 30/132,
Vivado-supported all four 10+11+3+1+4 = 29 of 22+26+21+23+19 = 111, known gaps 15+15+20+22+15 = 87
(the rows, id to bit, equal those of the full run)
$ PATH=<yowasp yosys 0.68 shim>:$PATH test/sv/run.sh <the 9 new probes and 13d> (CI's yosys; brew untouched)
test/sv: 10 probes, yosys 0.68, iverilog 13.0 (expect.tsv was measured with yosys 0.69+post, ...)
matches expect.tsv: 10/10
$ PATH=<the same shim>:$PATH SV_OUT=<chunk>.tsv test/sv/run.sh <the 132, in the same five runs>
matches expect.tsv: 27/27, 27/27, 27/27, 27/27, 24/24 (193 s, 186 s, 115 s, 113 s, 117 s; 0 BAD)
synthesis 69/132, all four stages 30/132, Vivado-supported all four 29/111, known gaps 87 (the same sums)
notes: 11 "netlist: testbench only, iverilog cannot compile the RTL", 2 "the CLI took counter for the top"
$ diff <the native rows, id to bit> <the 0.68 rows, id to bit> (identical, 132 rows)
$ PATH=<the same shim>:$PATH test/sv/eqv_check.sh <each of the 54 cases>
as expected: 54 of 54; red: none (172 s)
The breakers' netlists, round 6's eqv.sh, then today's:
digit counter, one LUT bit flipped EQV PASS: 196608 output bits compared, all equal (at 3072 compares to the RTL as dewfpga sim starts it, ...)
EQV FAIL: 3072 of 196608 compared output bits differ between the RTL and the netlist
digit counter lost (led = 1111) EQV PASS: 196608 output bits compared, all equal (at 9216 compares to the RTL as dewfpga sim starts it, ...)
EQV FAIL: 9216 of 196608 compared output bits differ between the RTL and the netlist
one-hot FSM, reset on sw[15] EQV FAIL: 10 of 196608 compared output bits differ between the RTL and the netlist
EQV PASS: 196608 output bits compared, all equal
the same, reset on btnC EQV PASS: 262144 output bits compared, all equal (both)
counter on led[3:0], assign the rest EQV FAIL: the RTL compiles but the equivalence bench does not
EQV PASS: 262144 output bits compared, all equal
the corpus Gray counter (6 FDRE) EQV PASS: 36864 output bits compared, all equal (at 9 compares to the RTL as dewfpga sim starts it, ...)
EQV PASS: 36864 output bits compared, all equal
counter with no start, led[3:0] x EQV FAIL: 49152 of 196608 compared output bits differ between the RTL and the netlist (both)
negedge set counter (FDSE_1, INIT x) EQV PASS: 196608 output bits compared, all equal (both)
The new eqv_check cases on round 6's eqv.py and eqv.sh:
simflip red EQV PASS: 196608 output bits compared, all equal (at 3072 compares ...)
simlost red EQV PASS: 196608 output bits compared, all equal (at 9216 compares ...)
swreset red EQV FAIL: 10 of 196608 compared output bits differ between the RTL and the netlist
partassign red EQV FAIL: the RTL compiles but the equivalence bench does not
xhold negset as expected (they pin what round 6 already did)
All 48 old cases on today's eqv.py and eqv.sh before the five were changed: 48 of 48 as expected (40 s).
eqv mutations on a copy of today's tree, and the cases that go red (54 cases; none without a mutation):
the x test dropped from the sim copy's rule xhold
STARTS_AT_1 without FDSE_1 and FDPE_1 negset
reset values ignored (every register started at 0) fillset fillaset fillasym
(the same with their resets still on sw[15]: all five green, the press hides the start)
the sim copy counted as resolved from the start simlost romstart
the excuse per compare again simflip simlost
the excuse removed romstart
switch resets not pressed swreset
no bit-by-bit start (--bits ignored) partassign
Runner and test/run.sh mutations, the check that sees each:
the rtl stage's exit code not required st_rtlexit: PASS on the runner, FAIL on the mutant
(the runner on 07 with the planted $error: BAD 07_enum_packed_array rtl fail)
the runner prints no score line sv_rows: 'test/sv/run.sh printed no score line, its rows make it
'synthesis 1/2, ...'', fail=1 (round 6's sv_rows: fail=0)
a home-folder path appended to test/run.sh round 6's personal_paths: 0 lines; today's: 1 (test/run.sh:469)
13d's testbench, all 432 single-bit LUT INIT flips of the product's netlist, in one simulation (408 s):
caught 414 of 432 (the breaker's count with the old testbench: 308)
the 18 left: LUT _32_ (it drives led[12]) bits 6 7 12 13 14 15 18 19 22 23 24 25 26 27 28 29 30 31
input combinations of _32_ that never occur, every grid value x every row and column (1048576 cases, 7 s):
6 7 12 13 14 15 18 19 22 23 24 25 26 27 28 29 30 31
the breaker's flip (LUT 3, bit 27): old testbench PASS, new FAIL: grid=84f5 sw=c45a led=54f5 want=44f5
The new probes' testbenches, on the RTL (iverilog) and with one edit:
93 PASS the plain counter as top FAIL: after 1 clocks led=1, want 0
94 PASS sw[7:0] ** 3 FAIL: sw=0002 led=8 want 4
94 PASS sw[7:0] * 2 FAIL: sw=0001 led=2 want 1
95 PASS led = {sw[14:0], b} FAIL: sw=0001 led=000X
96 PASS assign led = sw FAIL: 2 of 2
96b PASS assign led = sw FAIL: 16 of 16
97 PASS writes the address, not the data FAIL: address 0 led=0000
97 PASS reads the next address FAIL: address 0 led=000a
98 PASS INV passes a through FAIL: sw=0000 led=0000
99 PASS the ROM read at ~a FAIL: address 0 led=00a6
31b PASS posedge instead of negedge FAIL: the rising edge moved q: led=0000
31b PASS the set loads 0 FAIL: the set on the falling edge: led=0000
31b PASS the preset loads 0 FAIL: the preset, before any edge: led=0003
Measured apart for the new rows:
93 as lab4.sv ERROR: 2 modules could be the top (nothing instantiates them): counter, top. Name it: dewfpga bit counter
corpus 061 (its own XDC) pnr ok: 6 LUT, 6 FF, 830.56 MHz (PASS at 100.00 MHz); the plain counter's .bit, exit 0, no 'top module:' line
94 with 16'd3 ** sw[2:0] the same 'no Bels remaining of type '$pow'' and 'does not fit'
94 with 2 ** sw[3:0] pnr ok: 16 LUT, 0 FF; with sw[7:0] * sw[7:0]: pnr ok: 0 LUT, 0 FF (a DSP48E1)
95 with b unused pnr ok: 0 LUT, 0 FF, no clocked paths, timing not applicable
96, nextpnr-xilinx on the product's top.json: Constraining 'led' to site 'IOB_X0Y3', 'sw' to 'IOB_X0Y11'
(the sites of led[0] and sw[0] in a 16-bit design)
96b, the same with its own XDC: exit 0, sw[1] to sw[4] on IOB_X0Y12, Y10, Y9, Y7 (the sites of sw[1] to sw[4])
97 with rom_style = "block" on an initialized ROM: RAMB18E1: 1/150, pnr ok: 0 LUT, 0 FF, no clocked paths
98 as GND, VCC, MUXF7: cells_sim.v:28, :24, :342: ERROR: Re-definition of module ...; as BUF: pnr ok: 1 LUT
99 untyped: pnr ok: 7 LUT, 0 FF; its netlist: the testbench PASS, EQV PASS: 1048576 output bits compared, all equal
The port-line fix (check_xdc.py --fix-ports) on eight placements of a comment, 1 = the line was fixed:
// a comment after seg, at the end of the seg, line 1 (the comment itself ends in a comma)
// the segments at the end of the seg, line 0
// ... on a line of its own between the two 0
/* ... */ on a line of its own between the two 0
/* ... */ between seg, and logic dp on one line 0
/* ; ... */ after seg, 0
// ... after logic dp, 1
/* ... */ before the comma after seg 1
Not verified.
- CI has not run this tree: the main session pushes it. Under CI's yosys the probes were checked with the YoWASP build of 0.68 (a scratch venv, Homebrew untouched), not the Homebrew bottle
0.68+postthat CI installs.63bcrashes yosys 0.69+post and stops at an internal cell check under YoWASP 0.68; what the bottle does is not known, and the row quotes neither, so it holds either way. - No board was plugged in during #2, so
dewfpga flashwas not run;35_mmcm's bitstream in particular was never tried on a board. - What Vivado does with the 18 unverified probes (
03,03b,04,05,37,37b,39,46,48,55,63,63b,66,77,80,95,98,99): no Xilinx document or corpus log settles them;04and05have only forum sources. - Which top Vivado picks by itself when two modules qualify and the project names none: the two corpus projects of
93's shape name theirs. How Vivado names the pins of a one-bit vector port (96): no corpus source uses one; the row rests on the guide and IEEE 1800. 97's block RAM, and arom_style = "block"ROM, on a board: never flashed; that the netlist stage cannot check them is by construction (no model), not a sign they are wrong.- Things the breakers tried whose Vivado side no source here shows: a typedef header included by two files,
output logic Cout; reg Cout;, a testbench that assigns a variable a DUT output drives, a non-constant$clog2, twoassigns to one wire. - Which of the two synthesis runs in the course example project's log
57's bitstream came from. - The two corpus designs whose netlist differs from the RTL only through registers the RTL leaves
x(round 3): judged start-value artifacts, not proved cycle by cycle.
Not done here.
- The top finder on
26, the constructs the probes record (now 82 Vivado-supported failures,56b,90,94,96and96bamong them), and the XDC'sget_portswildcard and list forms: #4. - The silent wrongs,
03,22c,22d,22f,37b,39,51c,58,59,81and93included, the untimed DSP48E1 paths and block RAM (97), a module defined in two files, and the missing rebuilds: #3. - The messages of
12c,33c,40b,52,53,70,77,80,85,94,95,98and99(and the "with -nobram every memory becomes LUTs" of the "does not fit" message, false for97), iverilog'srepeatwarning (84), the awk program printed on a yosys crash (63b), twoassigns to onewire, and the async-reset rule's advice for80: #5. The plan called that rule dead and had #5 delete it; the plan now says it is live (80), so #5 fixes its advice instead. (The guide's sections 7 and 8 now say what the CLI does today; #5 changes what it says.) 75: Vivado writes the bitstream when timing fails;CLAUDE.mdrecords "timing FAIL = hata". Which one wins is the owner's decision, and #4 is where it lands, with89(a divided clock checked at 100 MHz).- A test that looks inside the bitstream, the garbled and the internal clock names on the pnr line (
31b's falling-edge clock too), and a Vivado-style testbench without$finish: #6. dewfpga simwith two design roots namingdewfpga bitin its advice: #12, with top selection.- README, README.tr and the cli page still say "46 checks". #6 locks the number.
#3 · Silent wrongs stop the build · 26–27 September
In numbers. A silent wrong is a design that builds a bitstream and does not do what the simulation showed: expect.tsv rows with bit=pass and netlist=fail. Before #3: 17. After: 2 (35, an MMCM, and 97, a block RAM: their netlists have no simulation model, the circuits are right). Of the 15 others, one became right (33, a PMOD pin tied to 'z and read back) and 14 stop with an ERROR that names the student's file and line and says how to fix it; no .bit is written. Three probes that used to fail now pass all four stages (26 an array of instances, 33, 56 a file-scope enum), so the Vivado-supported probes passing all four stages went 29/111 → 32/111 and all four stages 30/132 → 33/133 (one probe added, 95b). Synthesis 69/132 → 57/133: the silent wrongs now stop there. Known gaps 87 → 79. test/run.sh: 178 passed before, 187 passed, 0 failed, 79 known gaps after (9 min 10 s). dewfpga bit on the blink example: 4.6 s before, 4.1 s after (three yosys runs instead of one: a preprocessor dump, elaboration, synthesis; measured once each, warm).
What the student sees. test/sv/39_undeclared_name (a typo, summ for sum), dewfpga bit, before:
design.sv:4: Warning: Identifier `\summ' is implicitly declared.
Warning: Wire top.\summ is used but has no driver.
pnr ok: 0 LUT, 0 FF, no clocked paths, timing not applicable (full log: top.log)
top.bit 2.2 MB
exit 0
after:
design.sv:4: Warning: Identifier `\summ' is implicitly declared.
ERROR: design.sv:4: summ is not declared anywhere and nothing drives it: yosys made it a 1-bit wire stuck at x and built the design without it (dewfpga sim refuses this code). Declare it, or check the spelling (a typo of a declared name).
exit 2
05_two_block_driver (one register written from two always blocks) printed 22 lines of yosys warnings and top.bit 2.2 MB, exit 0; now, after the first warning:
ERROR: design.sv:3 and design.sv:4: cnt is written from two always blocks (design.sv:3 and design.sv:4). Drive each signal from one always block: yosys resolved the conflict to a constant, so the board would not do what the simulation showed.
and no bitstream. 93_two_roots_file_name (the plain counter and the board's top in one file named counter.sv) built the plain counter, counter.bit 2.2 MB, exit 0, without a word; now:
ERROR: 2 modules could be the top (nothing instantiates them): counter, top. Name it: dewfpga bit counter
exit 1
What changed, one mechanism per class. Product files only: templates/Makefile (111 → 252 lines), templates/check_xdc.py (192 → 318), bin/dewfpga (162 → 164).
- Undeclared and unresolved names (
39,58,22c,22d,22f). yosys warnsIdentifier ... is implicitly declaredwith the file and line, then builds a 1-bit wire with no driver.-noautowirewas tried first and dropped: it also refuses the legal implicit nets of76(a port connection.y(w), a net declaration assignment; IEEE 1800-2017 6.10). Now a plain implicit name is an error only when yosys then says nothing drives it; a dotted name (u_fsm.state,c.q) is an error unless its prefix is an interface port (22_interface_modportkeeps building: yosys warns for those while parsing and resolves them inhierarchy). The message tells an interface member from a hierarchical name; the interface one says "not supported yet (#4)". - Two always blocks writing one register (
05), and a register written by an always block and a continuous assign. yosys'multiple conflicting driverswarning names cells, not lines, so the recipe now runs yosys twice:read_verilog; hierarchy -top; proc; write_rtlil top.il, thenread_rtlil; synth_xilinx .... The awk reads the cells'srcattributes fromtop.iland prints both lines. A conflict whose drivers have no always-block line (two assigns to a tri bus,52) is left to nextpnr's verdict as before. - Async load from a signal (
03,03b). yosys emulated it with FFs and a mux ("Async reset value ... is not constant!") and03b's netlist was a combinational loop. Any$aldffcell left afterprocis now an error with the always block's line and the fix (load with the clock, or reset to a constant). A reset branch that loads a parameter or a localparam still builds. $isunknownin a design (59). yosys folds it to 1; on the board it is 0. Looked for in the code yosys' preprocessor hands to the parser (read_verilog -ppdump), so a/* */comment or an`ifndef SYNTHESISblock does not trigger it (the first version scanned the raw text and refused both; break round 1 found it).`file_push/`file_popin the dump keep the file and line of an`include.- A module with ports that calls
$finish(81) was taken for a testbench, so its submodule was built as the whole design (counter.bit, exit 0) andsimprinted nothing. A testbench is now a module with no ports;$finish/$stopin a module with ports stops sim and bit at that line (Vivado ignores it, UG901 Table 21; yosys stops on it, #4 makes it ignored). Under`ifndef SYNTHESISit is allowed (round 2). - Two modules that could be the top (
93). The file-name tie-break is gone, and so is sim's "prefer the root a testbench instantiates": sim and bit give the same answer, and stop naming both. The.xdc-name preference stays and is printed (top module: X (named by the .xdc)). - A declaration initializer that reads inputs (
37b,logic [3:0] sum = sw[3:0] + sw[7:4];). yosys takes it as the power-up value; the LEDs never follow the switches. The scanner stops at the line and printsassign sum = .... A constant from aparameter/localparamof any type is a legal start value and builds (round 1 foundlocalparam int STEPrefused; fixed); a register with such an initializer gets the register-style fix (load it under its reset). - A module defined in two files (
95b, new probe:lab4.svand a backuplab4_old.sv). Stops naming both files and lines. Vivado column: unverified (no log of that case in the corpus). - A package the tools were not given (
23c,pkg.svnext todesign.sv). The CLI hands iverilog and yosys the files that hold a module; a package file is left out, iverilog stops at a syntax error and yosys drops the import and builds every name as an undriven wire. A source scan before either tool names the reference's line, the file that declares the package and the fix (`include "pkg.sv"); a package declared in a module file that sorts after its user (zz_util.sv) is now read first (round 2). - A typedef of a 2-D packed array (
51c,typedef logic [3:0][6:0] tab_t;) used for a parameter or a net: yosys keeps the value flat andSEG[i]picks a bit instead of a row, silently. Stopped at the declaration, with the flat+:rewrite yosys accepts; the scan reads`included files first (round 2). - An inout tied to
'zand read back (33). yosys kept the constant z as the pin's driver and the read gave z. A z-only driver drives nothing: it is dropped from the elaborated design (top.il), and the pin is read like a read-only inout. The netlist now equals the RTL (EQV PASS: 78674 output bits). - Stale rebuilds (found in #2, rounds 1 and 2).
top.jsonandtop_simnow depend on the.memfiles named by$readmem, the files named by`include, and a.depsstamp of the file list, so an edited ROM, a changed include, a deleted or renamed testbench and a dropped design file rebuild. Measured with scratch folders before the change: each printed "up to date" or ran the old binary.
Tried and did not work. -noautowire (above). yosys check -assert for the two-block case: it also counts the "no driver" warnings legal designs print. connect -unset for the 'z pin: it rewires the read too and the LED reads x. A first .mem grep pattern with a literal ( broke make 3.81 ("unterminated call to function sort"). Parsing top.il in the awk's BEGIN block read an empty file (the pipe starts awk before yosys writes it); it is read lazily now. hierarchy -check in the elaboration run refused the UNISIM primitives (34 BUFG, 35 MMCM: "not part of the design"); the check is the synthesis run's, which has the cell library. The first two-block rule fired on 52's tri bus with "(line unknown)"; a conflict without an always-block line is not this error.
Break rounds. Two, each a student (up to 10 new designs against the claim) and a regression reader (the diff, the 31 protect probes, the legal neighbour of every new rule), then a fix agent per task and an integration; then a judge was added to the script for the next updates (this run was made before it). Round 1: 9 reports, 8 in scope (constant start value from localparam int refused; $isunknown in a block comment and under `ifndef SYNTHESIS; an interface port on its own line refused; the typedef scan firing on a comment; import pkg::* reported as a typo; always_ff + assign printing "(line unknown)"; sim's two-roots message saying dewfpga bit). Round 2: 5 reports, 5 in scope (the typedef scan missing an `included .svh; $finish under `ifndef SYNTHESIS; a package file sorting after its user; a struct member taken for a type name; a second "(line unknown)" line in a submodule). All reproduced and fixed, each with the legal neighbour re-run. Held in both rounds: undeclared names in a .v submodule, hierarchical names two levels deep and into a generate block, two always_ff on the same edge in a parameterised submodule, an active-low async load in a .v file, $isunknown in an included function, start-value spellings in reg, typedef'd and parameterised forms, two roots across .v and .sv, two always_ff writing different bits of one vector (builds), a reset branch loading a parameter (builds), dotted names of a packed struct (builds).
Tests. test/sv/run.sh on the 16 target rows: each row's stages and its today quotes were rewritten to what the product prints (the runner fails a quote no stage printed). Two runner checks changed because the product changed: "a new silent wrong fails with any yosys" plants its wrong row on 97 (it used 05, which the product now refuses), and the personal-path scan skips the gitignored .claude/ folder (the workflow's worktrees live there). The full suite ran three times in the main session: the first run found the 34/35/52 regressions above and the 26/56 gains; the second failed only on 22b's stale quote (its message changed: the interface scan now reads every .sv in the folder, not only the module files, so an interface in its own file is classed as an interface, and the row quotes what yosys then prints); the third is the line quoted in the numbers.
Found and left to later updates.
- #4: interfaces used from the instantiating module (
22c,22d,22f) and hierarchical names (58) compile;$finishin a design ignored as Vivado does (81);59$isunknownas 0.12b's named assignment pattern refused by iverilog and yosys (the judge-less round 2 hit it).52/70's cells. - #5:
22fprints 32 "no driver" warnings after its three ERRORs; the ERROR for39prints before the two warnings it is derived from;26's and58's messages have no error code yet; the sim message for two roots saysdewfpga bit <name>(it works for sim too). - #6: the
.depsstamps andtop.ilare new build outputs (dewfpga cleanremoves them; a student's.gitignoredoes not know them); a stray.svhnot`included but declaring a 2-D typedef of the same name as a 1-D one would trip the typedef scan (no probe has it);.memand`includedependencies are one level deep. - Not verified (DOĞRULANMADI): whether Vivado orders a package file before its user on its own (UG901 does not say; the product now orders it); Vivado's exact behaviour on
39(no corpus log; the message states the IEEE rule).
CI. The first CI run of #3 was red on 95b: the message named the two files in the other order (design_old.sv:2 and design.sv:1). The CLI's file list came from the shell glob, whose order follows the locale (on the CI runner _ sorts before .), so the tools read the files in a different order on different machines and messages named them differently. The list is sorted with LC_ALL=C now, and the twice-defined message sorts its pair; the second CI run is the one that counts.
Orchestration, for the record. One workflow: 3 build agents in their own worktrees, one integrator, 2 breakers × 2 rounds, up to 3 fix agents per round, 14 agents in 3 h 0 min, then the harness killed the last integrator (a 20-probe run is silent for more than 3 minutes; it had already applied both fixes). Lessons written into the script: at most 5 probes per call, fix agents copy the integrated product into their worktree first (each spent 10 minutes discovering that their worktree was at HEAD), one scratch folder per agent (two overwrote each other's repro), a judge with a reproduced criticism before the close.
#4 · What Vivado accepts compiles: yosys-slang as the second reader · 27 September
In numbers. Vivado-supported probes passing all four stages 32/111 → 59/111; all four stages 33/133 → 64/133; synthesis 57/133 → 108/133; known gaps 79 → 52; silent wrongs 2 → 2 (35, 97, no simulation model). test/run.sh: 187 passed before, 213 passed, 0 failed after (13 min 48 s; the probe stage is longer, every refused design now runs through two readers). dewfpga bit on blink: 4.1 s → 3.8 s (the first reader builds it; a design that falls to the second reader pays one more yosys run). dewfpga check has a tenth row, yosys-slang, and the install grew by one step. CI on the commit: 212 passed, 0 failed, 26 min 59 s (15 min 32 s for #3: the slang build on the runner is the difference).
The reader decision, measured first (4a). One agent ran the synthesis stage of all 133 probes under four front ends: (A) yosys' read_verilog -sv, today's; (B) read_slang (the yosys-slang plugin, commit 9676786, 23 September 2026); (C) slang first, read_verilog when slang refuses; (D) read_verilog first, slang when it refuses. Counted: Vivado-supported probes whose netlist is right (the testbench passes on the netlist and eqv.sh finds it equal to the RTL). A 50, B 80, C 87, D 88. B alone loses 8 that A builds right (21, 31, 31b, 32, 33, 34, 54, 84): slang refuses a blocking assignment after a non-blocking one and repeat in always_comb, emits $aldff for a constant async reset (which #3's guard read as a load from a signal), and lowers assign JA[7:1] = 'z to a $buf of x, so 33 reads x from the pin. D has none of these (they take the first path) and gains 38 the first reader refuses. The rule was fixed before the run: most right netlists with zero regressions among the 33 all-pass probes; D. Its holes, named in the same report and closed here: 60 (ref arguments, which slang drops: a silent wrong), the two-driver and async-load guards on slang's output, 01 (slang builds an always_comb latch as a LUT loop, not a latch). Install cost: one more cmake build in install.sh, 2 min 59 s for the whole install.sh re-run on this machine (every other step stamped, so that is the clone and the build), 8.9 MB of slang.so.
What the student sees. 16b_func_return (return inside a function), dewfpga bit, before:
design.sv:3: ERROR: syntax error, unexpected TOK_ID
exit 2
after:
note: top read with yosys-slang (yosys' own reader refused this code)
pnr ok: 7 LUT, 0 FF, no clocked paths, timing not applicable (full log: top.log)
top.bit 2.2 MB
exit 0
58_hier_name (assign led[1:0] = u_fsm.state;) was #3's error; now pnr ok: 3 LUT, 2 FF, 420.88 MHz, top.bit 2.2 MB, and the netlist equals the RTL. 01_latch_comb (an if without else in always_comb) stopped with "Give q a default value"; now:
warning: design.sv:3: latch inferred for q (an if without else in always_comb); Vivado builds it too, with the same warning; if you meant a register use always_ff, if a latch use always_latch
pnr ok: 0 LUT, 1 FF ... top.bit 2.2 MB
with one LDCE in the netlist, as Vivado builds it (UG901 Ch.5, Latches). 02_unnamed_inst (inv(sw[0], led[0]);) was an error; now the CLI writes the name into the file, as it already did for the course's seven-segment port line, and says so:
note: design.sv:5: named the instance: inv u_inv(sw[0], led[0]); (the standard and Yosys need it; Vivado accepts both)
11_enum_methods (s.next()) is still refused, now by both readers, and the student gets slang's line:
yosys' reader: design.sv:5: ERROR: Can't resolve function name `\s.first'.
ERROR: design.sv:5: s.next() is an enum method that steps through the values (next/prev). yosys does not build enum methods, and yosys-slang builds first()/last() only. Write the step out as a case on s ...
What changed.
- Two readers, one recipe (
templates/Makefile). The first path is #3's: the scans,read_verilog -svfor.svand plainread_verilogfor.v(Verilog-2005 in Vivado too:bitandfinalare names there,74),hierarchy,proc,top.il, synthesis, the awk guards. When it fails for any reason and$FPGA_HOME/yosys-slang/build/slang.soexists, the second path reads the same files withread_slang(--map-keyword-version 1364-2005for the.vfiles) and runs the same synthesis and the same guards on its output; a success printsnote: top read with yosys-slang (yosys' own reader refused this code). When both refuse, the student sees yosys' ERROR line and slang's diagnostic withfile:line:col, the source line and the caret (slang writes it only into the-llog under-q). When slang is not installed and the refusal is one slang may resolve, a note names the missing reader anddewfpga install. - Guards on the second path.
$aldffafterprocis the #3 error only when its async value is not a constant (slang emits$aldfffor constant resets too;31,31b,32build). Two drivers: slang keepssrcon both$dffcells, so the lines are named, sorted.refarguments: a scan before either reader stops at the line (60; yosys does not readref, slang drops it and the caller's variable never changes). A latch on the slang path: slang lowers analways_combifwithoutelse(or acasewithoutdefault) to a$mux/$pmuxthat reads its own output, andsynth_xilinxreports a logic loop through a$procmuxcell with theif's or thecase's line; that is an ERROR now, not a LUT loop in the bitstream (break rounds 1 and 2 found both shapes; the same design on the first path is the latch warning above). - The first path builds the latch. yosys 0.69's
proc_dlatchrefuses a latch in a process carrying thealways_combattribute (log_error), so the attribute is unset beforeproc; the warning names the line from thesrcattribute,synth_xilinxmaps the$dlatchto an LDCE.always_latchbuilds the same way. - The scanner (
templates/check_xdc.py,bin/dewfpga).--fix-portsnow also names unnamed instances (u_inv,u_inv_2, ..., never a name the file already has;#(...)blocks, instances insidegenerate,.vfiles), before sim and bit; strings, comments and testbenches are left alone, and a file with CRLF keeps CRLF (the old rewrite turned every line ending into LF). The port-direction fix sees through comments in the port list, including one holding a;(08b), and the range-only item in an always block (08c). A module nameddisplay, or a module name inside a$displaystring, is not an instance any more (40,40b); an instance is looked for only where a module item can start, so a function or a call that carries a module's name (function logic [3:0] inv(,= inv(sw)) is not rewritten (break round 1 found the rewrite corrupting such a file; fixed). The netlist detector refuses only what yosys cannot read (Vivado's funcsim headers) and lets the course's ready modules in netlist form through (57). - The toolchain (
install.sh,dewfpga check, CI, the guide). Step 6 clones yosys-slang at the pinned commit and builds it intobuild.new/;build/is replaced only onceyosys -mloads the new.so(a build that fails leaves the plugin you have; the judge found the first version deleting it before rebuilding). A hand-builtslang.sothat loads is stamped instead of rebuilt.dewfpga checkloads the plugin each time and tells a missing one from one built for another yosys (afterbrew upgrade yosys); CI checks that the plugin loads. The guide's by-hand path has the step with its real output.
Tried and did not work. tribuf -logic before synth_xilinx for the internal tri bus (52, 33c): nextpnr still finds $_TBUF_ cells (a buffer that reaches a port is kept); 70's $and/$or from wor/wand and 94's $pow were not reached (all three stay #5's "does not fit" message). Rewriting only the "Latch inferred" line as a warning: yosys raises it as an error for always_comb, so the design fell to slang and built the LUT loop. The first latch rule matched $mux only; a case without default is a $pmux (round 2). The first ref scan and the $isunknown scan fired inside strings and block comments (fixed: the $isunknown scan reads yosys' preprocessor dump; the ref scan is still text-based and a $display string holding , ref x stops the build: open, #5). 23c on the slang path was not measured raw: the package scan stops it before either reader, since the CLI hands both readers the module files only.
Break rounds and the judge. Round 1: the student breaker died after three tool calls (the disk filled: see below); the regression reader sent 3 in-scope reports (the slang-path latch as a LUT loop, high; the function-named-like-a-module rewrite, medium; ref in a string, low), all reproduced, the first two fixed. Round 2: 2 + 3 reports (a case latch on the slang path, high; a .v next to a .sv that needs slang read as SystemVerilog, medium; the plugin dormant as installed, medium; the wording "an if without else" for a case; a task call named as an instance, low); the first three fixed, the case wording fixed with the $pmux rule. The judge (an agent that did not build any of it) returned fix with 10 criticisms: the $pmux latch not yet in the main tree (it was in a worktree the disk-full integrator could not apply), the metric blind to a wrong slang netlist (below), the installer deleting a working plugin first, 11's row stale, a slang.so that exists but does not load, and the guide quoting a 217 s install next to a step that adds 4 minutes. Its process criticisms are in the orchestration note below. What held, in the breakers' words: a latch in a submodule in its own file on the slang path (ERROR with hold.sv:3), always_latch on the slang path (builds, 1 FF), four unnamed instances in one file with #(.N(4)) and a generate, an unnamed instance in a .v with the course port line, two always blocks and an async load on the slang path (both ERRORs with lines), $isunknown on a design only slang reads, nested and generated latches, an interface design with a constant async reset (4 FDCE, no false $aldff error), the 47 protect probes and the 6 canaries unchanged.
The metric was blind, and the suite changed for it. test/sv/eqv.sh listed the RTL's registers with read_verilog -sv alone, so for every probe only slang can read it printed EQV FAIL: yosys could not list the RTL's registers after the testbench had passed on the netlist, and 33 slang-built rows were recorded as bit=pass, netlist=fail: the suite's silent-wrong count went from 2 to 35 while the netlists were right (a scratch copy of eqv.sh with read_slang as the fallback: 16 EQV PASS, 16 EQV SKIP where iverilog cannot compile the RTL, 1 still unreadable, 74). The judge called it: for those 33 a wrong slang netlist would pass the suite as the recorded state. eqv.sh now lists the registers with the same two readers as the product, so the netlist stage sees slang-built designs. 45 rows of expect.tsv were then rewritten from the logs of a run with the fixed eqv.sh: the stage columns and a today column quoting what the product prints (the note: top read with yosys-slang line, the pnr ok line, the EQV PASS line, or the ERROR that stops it).
Tests. The full suite ran three times in the main session after the integration: the first run found 45 rows to rewrite and 7 CLI checks the product change had turned (an unnamed instance is a note now, not an error; a .v with SystemVerilog inside stops, as in Vivado; the runner's self-checks that planted their mutation on 01's and 02's old messages plant it on the new ones, st_score plants its gap on 11); the second found 5 rows whose quotes the runner's parser cut at a backtick, a double quote or a home path; the third passed 213 and failed 1, the self-check st_fixtext, whose mutation string had a colon the source does not have: fixed and reproduced by hand (the mutated copy prints the expected no stage printed it line); the full suite on the committed tree is CI's run. test/sv/eqv.sh lists the RTL's registers with both readers. .claude/rows-from-logs.py rewrites a probe's row from the logs of a KEEP=1 run (used for the 45 rows; it cuts a quote at an apostrophe, a backtick, a double quote and /Users/, which the runner's quote parser and the personal-path check would trip on).
Found and left to later updates.
- #4 (later):
11enumnext()/prev()(slang buildsfirst()/last()only);52,33c,70,94's cells;74ineqv.sh(a.vwithbit/finalas names:eqv.shreads it with-sv);23cwith the package file handed to slang; a.svhnot`included that declares a 2-D typedef (the scan would fire). - #5: the
refscan on strings; the "does not fit" messages for the cells above; aslang.sothat exists but does not load prints a dlopen error instead of the student's diagnostic; the guide quotes a 217 s install total next to the 4-minute step. - #6: a test that the bitstream of
01holds an LDCE;test/run.sh's check ".v file with SystemVerilog inside builds" now passes through slang (read_verilog refuseslogicin a.v, slang reads it): the check says the opposite of Vivado and should turn. - Not verified:
11's UG901 row is the one #2 recorded; slang'sfirst()/last()netlists were measured on a scratch design, not a probe.
Orchestration, for the record. 19 agents, 1 h 58 min: 3 build (45, 30, 30 minutes), 1 integrator, 2 break rounds, 3 + 2 fix agents, a judge, 3 fix agents on the judge's findings. The machine's data volume hit 100% during break round 1: 40 GB of .vcd files that #2's breakers had left in a scratch folder on 25 September, 26 GB of KEEP=1 work folders under $TMPDIR, 5 GB of the 4a measurement's probe copies; the round-1 student, the round-2 and round-3 integrators and the judge ran without a shell (ENOSPC on the harness's own output file). The round-2 and round-3 fixes were applied by hand from their worktrees in the main session after the disk was cleared. The judge's process notes, applied to the next update: the integrator must run the demo probes itself and quote them (11's row went out stale); a probe belongs to one task (08b was on two lists); a fix agent commits to a branch so the integrator merges commits, not worktree files; the guard must be measurable by the suite before the run (the eqv.sh reader was not); a 4 GB free-disk check before the run, and KEEP=1 work folders deleted by the agent that made them.
#5 · Every message in one shape, with its code and its page · 27 September
In numbers. Over the 35 probes whose messages this update owns (a KEEP=1 run of each, before and after, rtl.log and bit.log): lines the product prints as an ERROR, a warning or a note, 68 before, 70 after; of them, carrying a code in brackets, 0 → 59, and 36 of the 59 start with the student's file:line (the rest are the toolchain, the XDC as a whole, timing, or the two notes read-with-slang and no-create-clock). The 11 uncoded lines after are the tools' own lines, kept above the coded one (yosys' Multiple edge sensitive events, nextpnr's Unable to place cell, yosys' Failed to get a constant init value). The catalog: 10 pages → 78, one per code, generated from docs/errors.md. The four stage columns of expect.tsv are byte-identical before and after (md5 of the columns, both trees): this update changed words, not verdicts; Vivado-supported probes passing all four stages 59/111 → 59/111. test/run.sh: 213 passed before, 213 passed, 0 failed after (14 min 41 s; two rows were requoted after the run and re-run alone, CI runs the whole suite on the commit).
The shape. One line per problem: the student's file and line, the kind, the code, what is wrong, the fix in one sentence, the page.
design.sv:4: ERROR [undeclared-name]: summ is not declared anywhere and nothing drives it: yosys made it a 1-bit wire stuck at x and built the design without it (dewfpga sim refuses this code). Fix: declare it, or check the spelling (a typo of a declared name). https://nosey-dewdrop.github.io/dewfpga/errors/undeclared-name/
A problem with no place in the student's code starts with the kind (ERROR [board-not-found]: ...). yosys', iverilog's, nextpnr's and yosys-slang's own lines stay as they are, above the product's line; when the product already made a coded ERROR, slang's diagnostic is shown as a plain yosys-slang: ... line, not a second ERROR. Warnings and notes carry a code too (warning [latch], note [unnamed-instance]). The code is the page's folder under /errors/; the ten pages that existed keep their slugs.
What the student sees. 77_initial_from_signal (initial num = sw;), dewfpga bit, before:
yosys' reader:
ERROR: Failed to get a constant init value for \num: \sw
ERROR: design.sv:3:17: reading net state during design initialization unsupported (yosys-slang)
after:
ERROR: Failed to get a constant init value for \num: \sw
design.sv:3: ERROR [initial-from-signal]: `num = sw` here is a start value, not a wire: it reads sw once, at time 0 (IEEE 1800-2017 6.8), and the chip has no power-up value that follows a signal, so yosys stops. Fix: for a wire write assign num = sw; for a register give num a constant start value (logic [15:0] num = '0;) and load sw with the clock (if (btnC) num <= sw;). https://nosey-dewdrop.github.io/dewfpga/errors/initial-from-signal/
31b_negedge_set's pnr line said 337:slice$1535.C': 358.68 MHz; it says clk 358.68 MHz. 04_dual_edge got "a signal is written from two always blocks with different edges"; it gets design.sv:3: ERROR [dual-edge]: this always block is sensitive to both edges of clk (@(posedge clk or negedge clk)), so cnt would change on every edge: no flip-flop on the chip does that ... Fix: clock the block on one edge. 33c (a tri-state buffer on an output port) got nextpnr's no Bels remaining of type '$_TBUF_' and "the design does not fit"; it gets design.sv:4: ERROR [internal-tristate] naming the z lines and saying that Vivado converts such a driver to logic and this chain cannot yet.
What changed.
- One formatter (
templates/Makefile): an awk functionfmt()prepended to every awk program, and the same shape in the shellecholines; every message the Makefile prints goes through it (the yosys filter, the nextpnr filter, the timing verdict, the scans, sim's testbench-missing, timeout,$errorand exit lines, flash's board-not-found). - Lines for the line-less.
04dual edge (the always block's line from thesrcattributes, as the two-driver rule reads them),53(an async reset ORed with a synchronous clear: one block, told from the two-block case, with the shape of the fix),77(the declaration line), a module whose file is missing (module-not-found, the instantiation's line by a grep of the sources), a yosys crash (63b: the coded line names the file, and the awk program is never printed again). - The real cause for the misleading ones. nextpnr's
no Bels remainingis not "does not fit" unless it is a real LUT/FF overflow:$_TBUF_isinternal-tristate(with the z lines),$and/$orfromwor/wandand$pow(power-operator) name the construct, an empty module's blackbox (95,empty-module) names the module and its line; the-nobramsentence says what is true for a block RAM attribute (97). - The pnr line names the clock. A resolver reads the clock net back from the design's json (only the module named
top: the first version read the cell library's modules too and printedVAUXN) and prints the student's name (clk, a dividedslow), ora clock derived inside the design (file:line).31b's falling-edge clock printsclk, notclk (falling edge): yosys puts the inversion on the flip-flop's parameter, not on the net (open). - The scanner and the CLI (
templates/check_xdc.py,bin/dewfpga): the scanner's problems, the--fix-portsnotes, the XDC checker (ports with no pin now name the XDC line where one exists, the case typo the port's declaration; the wildcard and list forms ofget_portsthat Vivado accepts,96,96b, getxdc-get-ports-form, which says the form is not read yet) and everydieline (no source file, a space in a path, a BOM, no testbench, no or two.xdc, the toolchain missing, an unknown option, running outside the folder),check's summary line,uninstall's lines.dewfpga simwith two roots saysdewfpga sim <name>. - The catalog (
docs/errors.md,docs/errors-build.sh,site/errors/): one## codeentry per code (what the student sees, why, the fix, an example line) rendered into the pages' shell by pandoc, plus an index page; the ten old pages were moved into entries word for word and regenerated with the same text (a text diff, three<pre>blocks fixed by hand). The guide's section 7 is now "Which errors does the CLI stop with, and what do they mean?" with the shape explained once; its old anchor#7-which-course-file-breaksis kept as an alias (the 20 September post may point at it). README, README.tr and the cli page point at the catalog. A link check over the site tree: 210 links in 16 files, 0 broken.
Break rounds and the judge. Round 1, the student: 5 findings, all reproduced (an empty submodule with a parameter built silently through slang, its outputs x, high; the textbook tri-state mux written inline on a tri net reported as two always blocks, high; a dual-edge list spanning two lines reported as an async reset with a clear, medium; 3 ** sw[2:0] reported as a signal base, medium; the .v note after a coded error on plain Verilog, low). Round 2: 5 + 2 (two z drivers in a generate loop and always @* plus assign on one signal both reported as two always blocks, high; an undeclared name inside a generate sent to the unsupported-construct page, medium; the latch warning saying "an if without else" for a case, low; a ** message quoting an expression not in the file, low; two inline z drivers under yosys' own reader named by a yosys-internal net, medium; 33c's message claiming Vivado converts a top output's driver to logic where UG901 says OBUFT, low). The judge (an agent that built none of it) returned fix with 6 criticisms, 5 reproduced and in scope: an async reset with the wrong polarity (posedge btnC, if (!btnC)) reported as "loaded from a signal" with a fix naming rst, load, sw that are not in the design (high); the same message hardcoding (posedge clk or posedge btn) after 16 raw Emulating async set + reset lines (medium); timing-not-met advising create_clock -period 20.000 (50 MHz) to a design that reaches 33.61 MHz (medium); a case latch at top level called an if (medium); a driver conflict in a submodule printed twice, q and u_cnt.q (low). Three fix rounds closed all of them: the driver conflicts are named by their source lines and their real kind (always-and-assign, two-assign-drivers, tristate-to-logic), async-reset-polarity is a code of its own, the END-branch message uses the student's names and edges, the Emulating lines are folded, [latch] tells case from if, and the timing fix proposes a period the measured frequency allows. Held: the 35 targets and the 6 canaries with every stage unchanged, the CLI checks that grep the messages (rewritten by the integrator), two-file designs after round 3.
Tried and did not work. A bare # inside a grep pattern in a make variable starts a make comment and cut the awk program on every probe (\#). The first clock resolver read every module in the json. The first 5C link check flagged /dewfpga/sim/ and /dewfpga/templates/*, which CI copies next to site/ (the check resolves against site/ then the repo root), and the query string of ?view=tb. The .v note printed after a coded error that already named the cause (dropped there).
Tests. The suite's message greps (the CLI checks that look for a message, and the runner's self-checks that plant a mutation in a message) were rewritten by the integrator to the new lines; 44 rows of expect.tsv were requoted from logs after the build, 2 more after the judge's fixes (03b, 22b), all with .claude/rows-from-logs.py, which now recognizes the file-first shape. The full suite: 213 passed, 2 failed on the two stale rows, then those two re-run alone: matches expect.tsv: 2/2.
Found and left to later updates.
- #4 (later):
96/96b: yosys' json carriessingle_bit_vectorandoffseton those ports, so the XDC names could be built from it and both would build; not done here (the guard forbade a verdict change).11enumnext(). - #6: no probe exercises
module-not-found; the XDC checker's cap at 12 missing-port lines is untested; the flash lines are in the Makefile, tested only without a board. - Open in the messages:
31b's clock printsclkwithout "falling edge";33c's Vivado claim is the UG901 tristate paragraph, not a log. - Not verified (DOĞRULANMADI): the XC7A35T counts in the
does-not-fitmessage (20800 LUTs, 41600 FFs) are from DS180, not re-checked; UG903'sget_portspattern rules quoted inxdc-get-ports-form; the PDF's print layout after the new section 7 (14 pages) was not looked at.
Orchestration, for the record. 15 agents, 2 h 22 min, no agent died: 3 build (45, 35, 35 minutes), 1 integrator, 2 break rounds (student + regression reader), 3 fix agents, the judge, 3 integrations. The integrators committed in the main tree (four commits, squashed into this one). The judge's process notes, applied to the script: the diff is read against the update's base commit, not HEAD (the update may already be committed); the metric must reach the judge as a number, not as rows (the coded-line count over all 35 targets was lost between the integrator and the judge and was re-counted here); a message with a formula in it (create_clock -period) is checked against the number printed next to it.
#6 · The CLI's untested paths get checks, one number for the count, a test that reads the bitstream, the board test ready for the board · 27 September
In numbers. test/run.sh: 213 passed before, 237 passed, 0 failed after (15 min 7 s; the 24 new checks add about 35 s), plus one SKIP: flash on the board: no board: plug the Basys3 in and run test/run.sh again. test/run.sh --list prints every check's name, 133 probes and 293 checks (the two clean-install checks and the board check are counted on every machine, so no single plain run reaches 293: the header says which ones a run skips). README, README.tr and the cli page quote 293 and 133 from --list, and a check compares them. Vivado-supported probes passing all four stages 59/111 → 59/111 (this update changes tests, not verdicts); the stage columns of expect.tsv unchanged. test/bit-check.sh reads the frames back out of blink.bit and compares them with the .fasm: bit ok: blink.bit holds the 5408 frames of blink.fasm (105 non-zero), read back from 1 FDRI write(s); another design's fasm, a .bit cut in half and a .bit with one bit flipped in a used frame are refused. The board checks are written and skip without a board; their first real run is Damla's, with the Basys3 on USB.
6A · The CLI's untested paths get checks; test/run.sh --list prints the count the docs quote.
test/run.sh --list prints every check's name, then 133 probes and 289 checks, and exits 0 without running anything; ONLY=regex test/run.sh runs the checks whose name matches (the probes are skipped). README, README.tr and site/cli quote 289 checks / 289 kontrol and 133 SystemVerilog probes, and one check greps the three files for the two numbers --list printed, so a stale number fails the suite.
Paths that had no check before (15 in == error paths, 3 in == rebuild rules): bit src/x, bit --foo, an FPGA_HOME with a space, a UTF-8 BOM, a testbench without $finish (SIM_TIMEOUT=2), a testbench ending with $stop, a _tb.v testbench (Verilog-2005), a missing IOSTANDARD, a failed build removing the old .bit, uninstall removing what install built and keeping the rest, uninstall with nothing installed, a design only yosys-slang reads, the note naming dewfpga install when slang.so is absent, two clocks named on the pnr line; a changed .svh include rebuilds and the .bit and .frames differ byte for byte, a changed .mem ROM rebuilds and the .bit differs, a renamed testbench recompiles (sleep 1.1 first: make compares whole seconds). personal_paths adds --exclude=.git (in a worktree .git is a file holding a home path).
Integration: 6A and 6B both edited test/run.sh and clashed in three hunks (the shellcheck line, the old flash checks, the section after the rebuild rules); resolved by hand keeping both. 6A's count was 284 before 6B's block (+5 checks without a board); the number on the pages is what --list prints after both: 289. Run alone: ONLY='new \+ dewfpga bit|unnamed instance|blink\.bit|cut in half|flipped|flash|quote the numbers|shellcheck' test/run.sh -> passed 11, failed 0, known gaps 0, with SKIP flash on the board: no board: plug the Basys3 in and run test/run.sh again. Untested tonight: the flash check with a board (no board plugged in), the full suite.
6B · A test looks inside the bitstream; the board test is ready for the board.
test/bit-check.sh <top.bit> <top.fasm|top.frames> reads the bitstream back. No test had ever opened a .bit: bit checked the file's size and that the .frames file was the same twice. Now the .bit is parsed as UG470 chapter 5 lays it out: the header up to the sync word AA995566, then the type 1 / type 2 configuration packets; the FDRI write is cut into 101-word frames (UG470 table 5-18, 7-series) and each frame gets its address by walking part.yaml's columns the way xc7frames2bit does (block type, top/bottom, row, column, minor; two zero frames between rows and block types). Every one of the part's 5408 frames is compared with what fasm2frames.py makes of the .fasm (a .frames file is taken as is; frames it does not name are zero). The one thing not compared is the 13-bit ECC field of word 50, which xc7frames2bit computes and the .frames does not hold. prjxray's bitread is not built in ~/fpga (only xc7frames2bit is), so the reader is 60 lines of Python in the script, on the venv's python and yaml.
What it printed (paths shortened):
- blink.bit + blink.fasm:
bit ok: blink.bit holds the 5408 frames of blink.fasm (105 non-zero), read back from 1 FDRI write(s), exit 0 - blink.bit + blink.frames: the same line, exit 0
- blink.bit + the fasm of another design (probe 07, a sw -> led state machine):
ERROR [bit-check]: blink.bit does not hold the frames of top.fasm: 82 of 5408 frames differ, first 3: 0x00000a00 word 24: bit 0x00000200, fasm 0x00000000; 0x00000a01 word 24: bit 0x00000040, fasm 0x00000000; 0x00000a04 word 62: bit 0x00800000, fasm 0x00000000, exit 1 - blink.bit cut in half (
head -c 1096057):ERROR [bit-check]: half.bit does not hold the frames of blink.fasm: truncated: a packet at word 45 says 547420 words, only 273929 are left (1096057 bytes in the file), exit 1 - blink.bit with one bit flipped inside a used frame:
ERROR [bit-check]: flip.bit does not hold the frames of blink.fasm: 1 of 5408 frames differ, first 1: 0x00000a04 word 74: bit 0x42000000, fasm 0x02000000, exit 1
test/run.sh: the block == inside the bitstream, and the board (one delimited block after the project template section, marked #6B), seven checks without a board, run alone against a prebuilt blink and probe 07's fasm: passed 7, failed 0. The five bitstream checks are the five lines above, on the blink dewfpga new built ($T/p) and the sw -> led design of the unnamed-instance check ($T/ui). The two flash checks moved here from the build section: dewfpga flash without a board has to exit 1 with ERROR [board-not-found]: board not found and no make: *** trailer, and the run then prints one line: SKIP flash on the board: no board: plug the Basys3 in and run test/run.sh again. With a Basys3 on USB (system_profiler lists Digilent) the block instead runs dewfpga flash on that blink and greps openFPGALoader's own success line, Done (the string in /opt/homebrew/bin/openFPGALoader; SRAM only, gone at power off). The old DEWFPGA_TEST_BOARD=1 gate is gone: a plugged-in board is programmed with blink. Not run with a board tonight: no board was plugged in. The board branch is written from openFPGALoader's strings, not from a run; the first run with a board is the check of that line. shellcheck -S style covers test/bit-check.sh now.
Test count: two flash checks before, seven checks in the block after: +5 without a board (+4 with one). test/run.sh was not run whole here (9 minutes); the block was run on its own.
Fix round 1 (6B-fix), two findings of the judges:
--listprinted 288 with a board, 289 without. The board branch held one check, the no-board branch two, and--listwalks whichever branch the machine is in, so the number the three pages quote (289) was wrong on the machine the board is plugged into, and the docs check failed there. Reproduced with a fakesystem_profilerfirst on PATH (test/run.shcalls it unqualified) that printsDigilent USB Device:test/run.sh --list | tail -1printed288 checksandONLY='quote the numbers' test/run.shprintedFAIL README, README.tr and site/cli quote the numbers --list prints (checks, probes). Now either branch is two checks: with a board,flash on the board: openFPGALoader programs it (its Done line)andflash on the board: exit 0, no ERROR line(dewfpga flash >out 2>&1 && ! grep -q 'ERROR \[' out). After:289 checkswith the fake board and without, and the docs checkPASSin both. The board branch is still not run against a board (none plugged in tonight).bit-check.shblamed the.fasmwhen the tools were not where it looked, and it looked only atFPGA_HOME/~/fpga, not at.fpga_homeasbin/dewfpgadoes. Reproduced:FPGA_HOME=/nowhere test/bit-check.sh top.bit top.fasmprintedline 25: /nowhere/venv/bin/python: No such file or directoryandERROR [bit-check]: fasm2frames could not read top.fasm. Now the script resolves the folder the waybin/dewfpgaline 18 does ($FPGA_HOME, else the repo's.fpga_home, else~/fpga), checks that the venv python,fasm2frames.pyand the part'spart.yamlexist before anything else, and names the missing one and where the folder came from:ERROR [bit-check]: /nowhere/venv/bin/python is not there (FPGA_HOME=/nowhere). Fix: run install.sh, or set FPGA_HOME to the folder that holds venv/, prjxray/ and nextpnr-xilinx/.With.fpga_homesaying/elsewhere(a copy of the script under a scratch root):ERROR [bit-check]: /elsewhere/venv/bin/python is not there (<root>/.fpga_home says /elsewhere). Fix: .... A real fasm2frames failure now carries its last stderr line instead of "could not read": on a file holdinggarbage line !!:ERROR [bit-check]: fasm2frames (~/fpga/prjxray/utils/fasm2frames.py) failed on bad.fasm: textx.exceptions.TextXSyntaxError: .../bad.fasm:1:14: Expected S or '=' or '{' or '#' or Identifier or Newline or EOF => 'bage line *!! '. Fix: the .fasm has to be nextpnr-xilinx's own output for xc7a35tcpg236-1; rebuild it with dewfpga bit.The sane run is unchanged:bit ok: top.bit holds the 5408 frames of top.fasm (192 non-zero), read back from 1 FDRI write(s).shellcheck -S styleon both scripts: clean.
Fix round 1 (6A-fix + integration), the other findings:
--listprinted a different number underFULL=1and on a board machine. Now every branch is listed whatever the machine: the two board checks, the two no-board checks and the two clean-install checks all count, sotest/run.sh --list | tail -1prints293 checksplain, underFULL=1, with a fakesystem_profilerthat printsDigilent USB Device, and with both. The run prints fewer PASS lines than that, never more: the branch the machine cannot run prints one SKIP line (SKIP flash on the board: no board: plug the Basys3 in and run test/run.sh again, orSKIP flash w/o board (2 checks): a Basys3 is on USB). The docs check readsFULL='' test/run.sh --listand finds theN probesline by pattern, not by position. README, README.tr and site/cli say 293 and why. Merged with finding 1 above: the board branch keeps 6B-fix's two checks (Done line; exit 0 and no ERROR line), so 289 + 2 board + 2 install = 293. With the fake board: the docs checkPASS, the two board checksFAIL(no board is plugged in), as they must.- The bitstream and board checks could not run alone under
ONLY=regex: they used$T/pand$T/ui, built by checks the regex had skipped.need_pandneed_uibuild them when missing.ONLY='blink.bit|\.bit |flash' test/run.sh: the five bitstream checks and the two no-board checksPASS,passed 10, failed 0(three rebuild/probe-runner checks match the regex too). The== buildand== multi-file designchecks still need the section's first check in the regex; the header oftest/run.shsays so. - Integration note. The tree before this update has no
--list:test/run.sh --listthere ignores the argument and starts the whole nine-minute suite (the before/after demo killed it after 25 s, exit 142). Its pages said46 checks.bin/dewfpga,templates/*,install.shandtest/sv/expect.tsvare untouched.
Fix round 3 (6B-fix), one finding of the judges:
- A board check that FAILs showed nothing a student could act on. The first on-board check piped
dewfpga flashintogrep -q 'Done'and the others wrote to their ownoutfile, so$T/out(whatbad()prints five lines of) was empty; the same for the seven bitstream checks. Reproduced with a fakesystem_profileron PATH printingDigilent USB Device,ONLY='flash on the board' test/run.sh:FAIL flash on the board: openFPGALoader programs it (its Done line)andFAIL flash on the board: exit 0, no ERROR linewith nothing under either,passed 0, failed 2; and in a scratch copy of the product withboard-not-foundrenamed,ONLY='flash w/o board':FAIL flash w/o board: board-not-found line, exit 1, bare. Nowtest/run.shhassay <cmd...>: it runs the command with its output kept in$T/sayfor the check's grep and printed too, so it lands in the check's output and under a FAIL; the nine checks of the block run the product through it.check()also prints anyERROR [line past the first five (shown), since the product's ERROR line comes last, after openFPGALoader's own lines. After, the same fake board:FAIL flash on the board: openFPGALoader programs it (its Done line)followed byunable to open ftdi device: -3 (device not found)/JTAG init failed with: unable to open ftdi device/ERROR [board-not-found]: board not found: the Mac sees no Basys3 on USB (openFPGALoader: unable to open ftdi device). Fix: plug the USB into the PROG port, switch the power ON, and try another cable or port. https://nosey-dewdrop.github.io/dewfpga/errors/board-not-found/, the same three lines under the second FAIL; the renamed copy shows itsERROR [board-missing]: ...line under its FAIL; a bit-check refusal made to fail (its "frames differ" wording renamed in a copy) showsERROR [bit-check]: .../p/blink.bit does not hold the frames of .../ui/ui.fasm: 111 of 5408 frames disagree, first 3: 0x00000a00 word 24: .... Unchanged where it should be:ONLY='blink.bit|\.bit |flash' test/run.shon the real product, no board: the five bitstream checks and the two no-board checksPASS, one SKIP line;test/run.sh --list | tail -1still293 checks;shellcheck -S style test/run.shclean. Still not run with a board (none plugged in).
Tried and dropped:
- Re-encoding the
.frameswithxc7frames2bitand comparing bytes: the header carries the date and time, and a byte compare says nothing about which frame differs. Parsing the packets does. - Comparing word 50 whole:
xc7frames2bitrewrites its low 13 bits (updateECCin prjxray'secc.cc,data[50] &= 0xFFFFE000), so the.framesand the.bitalways differ there. Masked. - Building
bitreadfrom prjxray's sources: it would write into~/fpga, which this update leaves alone.
Also in this commit. The check uninstall refuses FPGA_HOME=HOME ran the real CLI's uninstall against the real home (the judge read it: the day the guard breaks, the test deletes ~/venv); it runs against a scratch home now, with a scratch fpga and venv that must survive.
Found and left to later updates.
- Product (K2 or #4 later): a build that fails in the XDC stage (a missing IOSTANDARD, a wrong port name) leaves the previous
.bitnext to the sources (only the synthesis rule removes it); the.depsrebuild rules miss a change made within the same second as the previous compile (Apple's make 3.81 compares whole seconds: bitessim, neverbit); a block-RAM or$readmemhdesign with registers onposedge clkprintsWarning: No clocks found in designand0 LUT, 0 FF, no clocked pathsalthough it is clocked (DOĞRULANMADI why: possibly the sync read became LUTRAM output registers nextpnr does not time). - Tests:
bit-check.shaccepts a.bitwith garbage appended after the last packet (words that are neither type-1 nor type-2 are skipped as padding); the SKIP line sends the student to the whole suite for two 40-second checks (ONLY='new \+ dewfpga bit|flash' test/run.shruns them alone); the two on-board checks were run only with a fakesystem_profiler, never with a board. - Not verified (DOĞRULANMADI): whether make 3.81 on this Mac lacks sub-second timestamps or the copy does.
Orchestration, for the record. 15 agents, 1 h 27 min, none died: 2 build (40, 30 minutes), 1 integrator, 2 break rounds, 3 fix agents, the judge, 3 integrations; the integrators committed in the main tree (the first commit went out with the #5 CI fix; the three fix commits are squashed into this one). The judge's process notes: the metric "N passed before → M passed after" needed two full runs no agent was allowed to make (the main session made them: 213 → 237); two agents in one file (test/run.sh) cost a hand merge in three hunks; a before-demo command must be vetted against the before tree (--list did not exist there and started the whole suite); "the same number on every machine" shipped twice without a run on a second kind of machine (env -i would have caught yosys: command not found at line 83 and the board and FULL modes); a check that hides the product's output prints a bare FAIL (fixed in round 3 with say/shown); "each new check broken once" was promised and not evidenced in the integrator's report (the judge did three).
#7 · Safer installation and reproducible simulator distribution · 3 October
The installer checks the destination before replacing it. HOME, its parents and aliases, the filesystem root, Git checkouts, unreadable directories and unrelated nonempty folders are refused. An update reserves a private backup beside the destination and restores the previous installation if moving the new copy fails. If restoration or cleanup is blocked, the error names the retained backup. A failed first installation removes its partial copy when possible.
Uninstall checks and removes the same physical directory. A path containing a symbolic link followed by .. previously passed a check of one directory and removed entries in another. The reproduced scratch-HOME case now exits with ERROR [fpga-home-unsafe] and keeps both the home files and the decoy directory. CDPATH no longer redirects a relative installation path.
Only tracked files enter the release package and static site. scripts/package.py stages tracked package inputs and rejects symbolic links. CI and the mirror copy tracked site and project-template files. The mirror refuses uncommitted tracked changes and untracked files in Vite's public directory, including ignored dotfiles. Its temporary Vercel state is removed on failure as well as success. Fixtures proved an untracked template file entered the old npm package and is excluded by the new packaging command.
The simulator ships its license files. Its footer links to the actual MIT license, Icarus GPL text and generated third-party notices in the built distribution. The inventory identifies JointJS as MPL-2.0 and distinguishes installed dependencies from confirmed bundled code. The Icarus recipe pins a full source commit and applies the required JavaScript compatibility edit automatically. A clean rebuild with Emscripten 6.0.2-git reproduced all four shipped files byte-for-byte; node sim/src/tb/wasm/build/smoke.mjs checks compilation, includes, counter output, plusargs, VCD output and rejection of invalid source. Hashes and commands are in the build README.
Validation and limits. The first full local run passed 258 checks, failed 0, with 52 known compiler gaps. After the additional security fixes, the second full run passed 262 and failed 1: the personal-path scan found a local Python bytecode cache. All 133 probe outcomes were unchanged (108 synthesize, 64 pass all four stages, 59/111 Vivado-supported probes pass all four). After removing that cache and isolating two new uninstall tests from the real Homebrew link, the affected security, static, count and privacy checks passed 35, failed 0. The verification intentionally pointed the real CLI link at the tested checkout and confirmed it survived. --list reports 319 available checks; that is not the number executed on this machine. A builder's initial mutation claim was rejected because its fixture setup was absent; the independent corrected run passed 15 on the original and failed the intended check on the mutant. A physical Basys3 test, this commit's remote CI and deployment are not claimed by these local results.
#8 · Browser tests and checks before publishing · 3 October
cd sim && npm test now builds the simulator, serves it on a free local port and runs 31 real browser checks. It uses Playwright's installed browser by default, closes the preview, and returns failure for failed assertions or a timeout. On macOS/Linux, timeout cleanup also terminates browser descendants still attached to the test process. The old machine-specific executable path is gone. Both a cached-browser run and an independent default-browser run passed 31, failed 0; the latter used Chromium 145.0.7632.6. A forced timeout returned 124 and left no new Chromium process. Processes already reparented outside the test tree are a known limit of this cleanup; it is not a general process supervisor.
The Ubuntu CI job installs the locked dependencies and Chromium, then runs those browser tests. Pages publication requires that job and the macOS CLI job. The assembled Pages artifact and the rewritten mirror bundle must pass test/site-check.py before publication. Checks cover links and anchors, navigation presence and consistency, canonical metadata, sitemap membership, duplicate IDs, question punctuation and unexpected package files. The question rule is a language heuristic. Ten deliberately broken site fixtures now run in CI, including deleted navigation, missing local anchors and questions outside h1–h6; an earlier review showed those cases could pass silently.
The source check exposed 69 missing error-page sitemap entries. docs/sitemap-build.py now regenerates the sitemap from tracked indexable pages and is called by the documentation builders. Local validation after rebuilding both artifacts: 86 source pages, 0 failures; 88 Pages bundle pages, 0 failures; 88 mirror pages, 0 failures. The same-page anchor fix and the sitemap fix were checked together on rebuilt artifacts. Remote CI is pending for this commit; local browser results are not represented as Ubuntu results. Commands are in sim/test/README.md.
#9 · One navigation source, unchanged desktop layout · 3 October
docs/nav.py now generates navigation from one chip table, and docs/nav.css supplies the shared navigation CSS. The guide and error-page builders use the same source. Generated copies are checked by test/site-check.py; changes to an active-page marker, Turkish language label, chip link or generated CSS are detected. A second generator run is byte-identical across the 89 checked copies. Eleven unreferenced simulator debug/screenshot scripts were removed; the browser test runner and engine checks remain.
Measured at 1366px: all nine navigation crops have 0 differing pixels, and all eight static full-page captures have 0 differing pixels. The simulator's animated area differs below the navigation strip and is not claimed identical. At phone width, the CLI's nine differing pixels also occur between two unchanged baseline captures; simulator captures remain dynamic. An independent comparison of computed styles at seven widths found no non-custom-property change. Both documentation generators, site checks, ten fault-injection fixtures and the Vite build passed. Rebuilt Pages and mirror artifacts each contain 88 clean pages. Physical GUI and this commit's remote CI are not claimed by these local measurements.
#10 · Patch notes on the site and working navigation hit areas · 3 October
The seventh navigation chip opens /patch-notes/, generated from this file by python3 docs/patch-notes-build.py. The index lists updates newest first and #latest links to the highest numbered entry. The Turkish navigation calls it “yama notları”. The generator disables dollar-delimited mathematics: an independent review reproduced Pandoc treating a paragraph containing $finish as mathematics and corrupting its prose. Raw HTML is disabled too; code and prose remain text. The page's modification date comes from this file's last Git commit, not from an invented release date.
The review also found that the existing transparent desktop link boxes used percentage units for their vertical positions. At widths 900, 1024 and 1366, the visible chips' centres hit the navigation bar instead of their links; the home link was affected too. Container width units now place all eight link boxes over the artwork. Independent hit tests and real mouse clicks passed at 900, 1024, 1366, 1920, 390 and 320 pixels on five pages. At desktop widths, every changed pixel on the checked static pages lies inside the added chip; the six existing chips keep their artwork and positions. Mobile chips remain 44 pixels high. The simulator's animated content is not claimed pixel-identical.
Two generator runs are byte-identical. The final source check passed on 87 pages; rebuilt Pages and mirror artifacts each passed on 89 pages, 0 failures. The clean baseline and ten deliberately broken site fixtures passed, as did the Vite build. A root rerun verified 210 chip clicks across 30 page/viewport combinations, with no failed hit or click. A 320-pixel CLI table overflow was reproduced in the previous page too and is corrected in #11. Chromium is the measured browser; Safari, Firefox, physical phones and older browsers without container width units remain unverified. The page is long, mostly because of the historical #2 report; its newest-first index and latest link provide direct access. Remote CI and publication are separate checks.
CI evidence now available for #8: clean macOS installation and suite 261 passed, 0 failed, 52 known gaps; Ubuntu browser suite 31 passed, 0 failed; assembled Pages artifact 88 clean pages, 0 failures. Run: 37107944441. These results apply to that commit and do not claim that the later Vivado or VS Code changes passed.
#11 · Five CLI steps and accurate Homebrew diagnosis · 3 October
The CLI page now explains the path from Homebrew to a blinking Basys3 in five steps. The guide relies on Homebrew's normal Command Line Tools installation and retains the explicit xcode-select recovery command for a missing developer-tools check. Its top-module explanation now matches the CLI: hierarchy and a uniquely matching XDC can select a design; a file name alone does not silently select it. A design containing $finish is an error, not automatically a testbench. The existing standalone-package limitation is stated.
Both the downloadable installer and install.sh distinguish a missing Homebrew installation, an Apple Silicon installation missing from PATH and an Intel Homebrew installation. They print the relevant next step before downloading the toolchain. An additional root check found that an Intel brew already on PATH bypassed the first implementation: that case now stops before executing it and explains the Apple Silicon path. Tests use executable sentinels to prove that none of those diagnostic branches invokes the discovered Homebrew binary.
The final affected installer, security, static, privacy and documentation-count checks passed 24, failed 0. test/run.sh --list now reports 321 available checks; it is not a count of checks executed by that focused run. Independent review approved the original installer patch; the root's extra Intel-on-PATH cases passed for both entry points.
At 320 pixels, the toolchain table previously widened the page to 384 pixels. It now scrolls inside its own wrapper, and the page stays 320 pixels wide. Chromium checks at 320, 390 and 1366 pixels find all five steps with no page overflow. Wrapping the unchanged desktop table produces 0 pixel differences and identical table geometry in a controlled before/after capture. These are browser measurements, not physical-phone or board tests. The final commit's full CI remains a separate acceptance check.
#12 · Vivado projects and editor tasks that preserve user files · 3 October
Commands now discover a Vivado .xpr or .srcs layout and read the enabled design, constraint and simulation sets. Running from a nested source directory writes build outputs in the project root. The project's selected top is respected unless explicitly named. dewfpga tops lists candidates as module/file pairs without writing files. Missing design files and ambiguous active sets produce actionable errors; a problem confined to the simulation set stops sim, not bit, tops or clean.
Local include directories reach both readers. .v files retain Verilog-2005 keywords in Icarus and the slang fallback. The first implementation deleted a pre-existing top.kw directory and mapped a/b.v and a_b.v to the same temporary filename. Root counterexamples rejected it. Wrappers now use a privately reserved directory, numbered filenames and scoped cleanup. clean leaves other recipes' temporary folders alone. Probe 74's RTL stage now passes; its netlist stage remains a known gap.
dewfpga vscode builds a deterministic companion VSIX and installs user tasks only for the selected supported editor and named profiles with the companion available. JSONC comments and unrelated tasks survive. Removal checks recorded content hashes, keeps user edits, and rolls back task writes if the ownership record cannot be saved. Symlinked setup records are refused. Automatic install/uninstall hooks can be skipped with DEWFPGA_SKIP_VSCODE=1; explicit editor setup still works. Native tests use that flag and isolated fake editors so they cannot modify a developer's real profile.
Independent review reproduced Cursor's code being mistaken for VS Code and a failed profile retry losing its earlier ownership record. A further root case showed that a code symlink into Insiders must not be selected for a Code removal. These cases have specific regression checks. Profile metadata was checked against Microsoft's source; that is distinct from testing the real editor GUI.
Focused checks before final acceptance: task writer 69/0, companion 35/0, Vivado 82/0, setup 89/0. An additional eight editor-boundary checks passed in a corrected isolated copy. The first full run caught two flat-folder compile-line regressions despite those focused results. They are retained in the test history; the corrected full run passed 269, failed 0, with 52 known gaps, in 24 minutes 11 seconds, with the tested files unchanged. The command list contains 325 available checks, not 325 executed successes. Real GUI and Basys3 acceptance remain unverified. Remote CI is pending for this integration.
Release review then found the tracked-file package builder omitted the companion sources. The old archive now fails validation for all four missing files. The fixed archive builds a VSIX byte-identical to the source tree and runs vscode --print. Both assembled site variants pass: 101 pages, 0 failures each. CI now builds the companion from the unpacked release, so source-only success cannot hide this omission.
#13 · Accurate lint severity and fresh waveforms · 3 October
The companion offers an explicit lint-shim setup command. Icarus's specific constant-select sorry diagnostic becomes a warning; syntax errors, other diagnostics and exit codes remain intact. The prior global setting is restored only when dewfpga owns the current value; workspace overrides and user changes are preserved. Symlink, hard-link and copied-shim recursion cases are covered.
A successful simulation task opens a new or changed VCD directly in its working project root. Before/after file identity, nanosecond timestamps and small-file hashes prevent unchanged old waveforms from opening, including same-second cases. The installed Verilog extension declares a default VCD viewer; actual GUI opening remains unverified. Nested or absolute dumpfile destinations are not discovered automatically.
The top chooser uses the real Quick Pick lifecycle, remembers choices per project and cancels the task on Escape. The companion suite passed 35, failed 0, skipped 0 in the integrated tree. These extension features share the #12 installation and ownership boundary and are committed together. Full CLI acceptance passed 269/0 with 52 known gaps. Remote CI passed for the combined integration.
#14 · Machine-readable CLI results · 3 October
check, sim, bit and flash now accept --json. One result object carries a stable schema tag, process and child exit codes, bounded diagnostics and logs, source locations and artifact provenance. Text commands keep their original output. The timeout option bounds the child process group; parent SIGINT and SIGTERM also stop that group. A deliberately daemonized process outside the group is not covered.
Independent review rejected false success after reader failures or open orphan pipes, warning floods that hid errors, stale artifacts attributed to the wrong top, and relative artifact scans from a nested Vivado directory. Root follow-ups fixed similarly named old bitstreams and multi-driver diagnostics containing several source locations. Unknown top/artifact provenance remains unknown instead of guessed.
The integrated tree passed 30 tests, 0 failures, 0 skips, including real sim and bit commands, text-mode parity and interruption cleanup. Separate real XPR and XPR-less projects passed 10 checks for nested roots, caching, unrelated artifacts and source paths containing spaces. The preceding #12–#13 full CLI run passed 269/0 with 52 known gaps; it is not a full run of this newer commit. Remote CI for this update passed. No real board programming is claimed.
#15 · MCP tools backed by the checked CLI · 3 October
dewfpga mcp exposes check, new, sim, bit, flash and explain_error over stdio. Build tools return the CLI's structured result; protocol stdout stays clean. The offline catalog ships in the release. Installation creates a pinned, optional SDK environment with staged replacement and ownership-aware removal. No client configuration is registered automatically.
Independent review reproduced unbounded output buffering, contradictory JSON accepted as success, cancellation leaving another process session alive, a missing ownership marker during environment creation, and uninstall racing an active install. The corrected server bounds capture, validates schema/type/status agreement and tracks observed descendant groups. Root integration also refuses the entire CLI uninstall before removing toolchain files while the SDK lock is held.
The root's SDK-backed suite passed 24 tests, 0 failures, 0 skips, including the real JSON CLI, protocol discovery, cancellation and the uninstall lock. The combined installer/uninstaller, JSON, MCP and static checks passed 40/0 before the extra CLI lock case was added; that case separately passed. Independent review also built and removed the real pinned SDK in a temporary FPGA_HOME, with foreign files retained. The unpacked release also served real SDK calls for new, sim and offline error explanation; check correctly reported its temporary home had no toolchain. Both site bundles passed 104 pages, 0 failures. Remote CI requires the SDK instead of accepting skipped protocol tests. The first CI failed in the installer; the correction below passed remote CI. No physical board was programmed.
Process-tree cleanup depends on ps. A daemon that reparents before observation and PID reuse remain limitations. Tool annotations describe effects; they are not an authorization enforcement mechanism. Clients must obtain user authorization for flash.
#15 integration correction · 3 October
The first remote CI run failed before verification: the optional MCP installation used an undefined HERE variable. Installation now resolves its repository path once before using it. The new offline whole-installer tests reproduce that failure with MCP enabled and check success, optional helper failure, and explicit skipping (3/3 passed after correction, 2/3 failed before). The repeated-install test also checks exit status, and ShellCheck checks unassigned uppercase variables.
The combined #14–#16 local run exposed a second regression: without Python on PATH, the new MCP removal hook prevented removal of a CLI-only installation. That path works again; existing SDK or lock entries cause an explicit refusal that preserves all files when ownership cannot be checked. Tests cover both an SDK directory and a dangling lock symlink.
The initial full run was 272 passed, 1 failed, 52 known gaps, with unchanged tested sources. After both corrections, the targeted static, installer, SDK-backed MCP and editor suites passed 6 top-level checks, 0 failures. The corrected combined local tree (also containing pending #16 guidance) passed 273/0 with 52 known gaps in 18 minutes; tested source hashes were unchanged. Remote CI for the correction passed, including clean macOS installation, the full suite and Ubuntu browser tests. The earlier failed runs remain recorded as failures.
#16 · Project instructions and a measured repair experiment · 3 October
dewfpga new now writes AGENTS.md and a one-line CLAUDE.md import into the project. The instructions name actual CLI commands, structured result fields and exit codes, preserve ports and constraints, and reserve board programming for an explicit user request. The generated llms.txt links the guide and error catalog; regeneration and all referenced error pages are checked. The listed suite grows from 327 to 329 checks.
The replacement experiment ran 16 fresh Fable 5.1 sessions, one attempt for each of eight fixtures in each arm. Both arms passed 8/8 repairs and all 130 acceptance checks, including fresh builds, testbench mutations, port interfaces and netlist equivalence. Plain/guided observations were 50/41 tool calls, 516.87/550.92 process seconds, and 0/13 Bash calls containing --json. These numbers do not demonstrate a guidance benefit: the sample is small, two fixtures are CLI auto-repair controls, and most reference testbenches were visible in the input.
Root invalidated the previous batch after finding guidance in a shared ancestor and an explicit read by a plain agent. Its results remain in the repository as invalid. The replacement checked both lexical and resolved ancestors before every call; root found no plain-arm guide or reference access in the transcripts. One guided agent used an outside-project temporary backup of its own source; this protocol deviation is disclosed. The protocol is not a filesystem sandbox. See the full results and limits.
The combined local #15 correction and #16 product sources passed 273/0 with 52 known gaps. The only previously tested files changed afterward are the experiment README and manifest; product source hashes match that run. The experiment's 63 frozen harness files remained unchanged throughout the batch. Remote CI for a80556d passed: clean macOS installation, the full CLI suite, packaged commands, assembled site and Ubuntu browser tests. No physical board acceptance is claimed.
#17 · Browser compilation checks the design and clears failed runs · 3 October
Yosys errors now open their source file at the reported line. Missing module errors point to the instance. Synthesizable $display calls are removed with an explanation; a failed compile clears old outputs and DONE while POWER stays on. Edits survive a reload even when Run was never pressed. All five original regressions passed in Chromium and WebKit (5/0 each).
The worker checks the hierarchy, uses the Yosys reader with a Slang fallback and checks hardware without SIM before accepting the simulation netlist. Guards for undefined names, multiple drivers, non-constant asynchronous loads and unsupported source constructs remain active; latches produce a warning. Source file extensions retain Verilog-2005 versus SystemVerilog semantics. Package/interface dependencies, hierarchical references, temporary-name collisions and Xilinx library read state were corrected during review.
Root found a wrong interface output (00Zz) that the earlier direct-Yosys test did not cover. Flattening the actual worker netlist without scope-info cells fixed the DigitalJS boundary. Three actual worker netlists now pass their original native testbenches and 576 DigitalJS output comparisons. The additional engine-isolation suite passed 16 checks, including file collisions, a unit-scope enum, a user module named INV and four guard controls.
The last compilation-decision audit covers 133 cases: 124 matching decisions, 7 native-accept/browser-reject cases, 2 reverse differences and 0 infrastructure errors. The remaining rejects are unnamed instances, inherited procedural port direction, BUFG, MMCM, internal tristate, a netlist-form module and a bitstream cast. The two reverse differences are separate interface/package source files that the native CLI source selector omits; their browser behavior was tested above. These counts do not establish corpus-wide behavioral equivalence or hardware timing.
#18 · A workspace that keeps names, choices and edits · 3 October
A directory tree and freely named sources, headers, memory data and multiple XDC files replace the fixed-name workspace. Top and testbench selection use source content; ambiguous choices are shown instead of guessed. Active XPR filesets exclude disabled/inactive files. A selected constraints file follows renames, including case-only renames, and a deleted selection does not transfer to a new file with the same name.
Autosave preserves original BOM/CRLF bytes until a file is edited. Share links keep top/testbench/XDC choices, file roles, active file and bytes in a fresh browser profile; old links still load. A failed link offers ZIP export. Workspace unit checks passed 69/0. Review found and fixed choice loss during rename and stale choice inheritance after deletion.
#19 · Bring a lab folder and take it back to the CLI · 3 October
Open-folder and drop import retain relative source/header/memory paths and reject invalid imports atomically. ZIP export includes the selected project manifest; a real nested ROM project exported from the browser passed CLI sim and bit checks. Missing or external active XPR references are reported rather than silently omitted.
WebKit exposed an unreadable FileSystemEntry for an otherwise readable in-memory File. Loose files now use the direct File; directories retain entry recursion. Root also found that stripping a shared ../ prefix could turn an unsafe path into a safe-looking name. Such roots remain invalid. Independent review proved that the old drop-rejection test could pass on a read error without reaching validation; it now requires the invalid path in the diagnostic.
Physical directory-entry drag/drop remains unverified: browser automation did not expose usable directory entries. Recursion is covered by unit tests, and real browser folder-input and direct-file drop flows are covered separately.
#20 · Located diagnostics while typing · 3 October
Live checks mark source lines and list problems. Actual errors disable Run; warnings remain runnable. Each diagnostic has a non-clickable hint showing coming soon!. Header paths, missing includes and repeated messages now produce useful locations.
Independent review reproduced a valid design timing out behind 50 pending checks after 60 edits. Checks are now coalesced: the same probe posted 7 checks with at most 1 pending, and Run completed in 16 seconds. A check timeout now says the sources are unchecked, replaces the worker and leaves Run available to compile again. The old board path blocked valid Run; the old testbench path falsely said there were no problems. Both behaviors were corrected.
The reviewer's final source checks passed 24/0 and its UI suite 126/0. Root tightened an older check that accepted not running as valid recovery. Remaining limits: queue wait counts against the request timeout; a check timeout can interrupt a concurrent run; two identical missing-include names can locate the first include site. No network cold-start timing is claimed.
CI follow-up (3 October). A WebKit run completed the memory design but exceeded the old 45-second aggregate check at 46.2 seconds. That check mixed worker speed with queue depth. The regression now records actual worker requests and replies at the Run click, requires at most one preceding check and no new lint while Run is active, and keeps bounded completion plus a working circuit. Four injected small jobs demonstrate that the same count rejects a backlog. The control uses a small circuit, avoiding an unnecessary second large memory simulation. Derived timing assertions were rejected during independent review: the worker's compile counter excludes hardware validation, and inferred durations did not provide independent evidence. Product scheduling remains unchanged.
#21 · Board controls retain the state the student is holding · 3 October
Switches and momentary buttons support pointer and keyboard input. Releasing one input method no longer releases a button still held by the other; focus loss releases held controls. Source and board hover links use the bound port/pin, without mistaking a helper-module port for the top-level output. Mobile tooltips stay inside the viewport. Failed runs retain POWER and clear DONE and outputs.
Independent browser checks passed 72/0. The expanded WebKit gate initially failed because its touch-hold test called Chromium-only CDP. Chromium still tests a trusted touch hold; WebKit tests the same handlers with explicitly labelled synthetic touch pointer events. This is not physical Safari/phone acceptance. Board pitch still limits mobile target width; LED changes are not announced by a screen reader.
#22 · Explain slow counters and keep Max responsive · 3 October
Detected divider bits and threshold comparisons explain why a 100 MHz design looks still at browser speed and suggest an explicit SIM divider. Closing the advice panel persists across reruns until the advice changes. Threshold comparisons, including strict versus inclusive bounds and mirrored operands, were corrected. Finite and Max clock modes both use a bounded per-frame work budget.
The repeated two-counter experiment measured roughly 5.8–5.9 thousand UI-driven DigitalJS cycles/s. Its isolated DigitalJS kernel reached 12–13 thousand/s; a CXXRTL kernel compiled locally to WASM ran in Chromium at 198–206 million/s, with 14 output comparisons. This measures a precompiled browser kernel, not a dynamic browser C++ compiler or a general equivalence result. Divider advice was selected; down-counter hints remain absent and Max does not promise a physical 100 MHz rate.
#23 · Prepare simulator discovery and enforce its browser checks · 3 October
The simulator's noindex directive is removed, its source metadata enters sitemap validation, and the home page distinguishes the available simulator/testbench/CLI from the future browser bitstream compiler. Existing source checks plus deliberately broken metadata catch a restored noindex, missing sitemap entry and bad canonical.
CI now runs the UI, five original regressions, board and hardware worker suites on both Chromium and WebKit. macOS additionally checks actual worker netlists and engine isolation with native tools. The original expanded WebKit gate failed before the CDP fixture repair; that failure is retained. Final combined acceptance is recorded below. The main-only deployment guard remains; this development update does not publish a live site or establish physical-board or GUI acceptance.
Combined #17–#23 root acceptance passed in both Chromium and WebKit: source 24/0, workspace 69/0, UI 129/0, original regressions 5/0, board 72/0 and worker 17/0, with no page errors. The assembled Pages and mirror packages each passed 104 pages, 0 failures, 1909 links. Their CLI archive is byte-identical to the #16 archive already tested with real sim and bit commands. The required SDK-backed full CLI rerun passed 273/0 with 52 known gaps in 20 minutes 26 seconds; all product and test hashes stayed unchanged (only these release notes and their HTML changed). A first local attempt omitted the SDK Python setting, failed the required MCP check and was stopped; that attempt is preserved separately. Remote CI run 37129232619 then passed on the exact db5b855 head: both web jobs and macOS succeeded. CI CLI counted 271/0 with 52 known gaps; two version-dependent checks were skipped with the CI Yosys build. The Pages deployment job was skipped.
#24 · Newsletter consent and working management links · 3 October
The optional newsletter page (not open yet) records a versioned consent request and queues a confirmation message. No account is required. Patch notes become eligible only after confirmation. Confirmation and newsletter messages carry the actual unsubscribe token in their body and List-Unsubscribe header. Public configuration is empty by default, so collection stays disabled until the operator configures the existing project and completes the notice.
A new consent generation now rotates its management token and clears the old account binding. Previously, an old mail link and linked account could continue to control fresh consent. A late completion for confirmation T1 can no longer mark a newer T2 request as mailed: the service RPC checks the token it actually sent. Cleanup also preserves a newly mailed link's 48-hour validity instead of removing it when an older pending request reaches seven days. Removal requires an operated cleanup; these thresholds are not an automatic deletion promise.
The data and privacy draft (not open yet) separates application records from provider logs and identifies the controller, contact, processing and retention details still missing. It does not claim KVKK/GDPR compliance.
#25 · Mail attempts retain uncertainty and campaigns require an owner test · 3 October
The operator's sender now binds each idempotency key to its exact payload and logical mail. A lost response, ambiguous server failure or concurrent-key reply retains the same key within a bounded window. A later refusal cannot erase an earlier uncertain attempt, including within the same claim. Claims carry an ownership token so a delayed refusal cannot release a newer sender's claim. Quota accounting includes delayed acceptance and outstanding uncertainty. These controls do not claim exactly-once delivery.
The ledger limits dewfpga's own allocation. A live sender observes the existing Resend account's usage before each ledger claim; unavailable usage or insufficient operator-chosen headroom defers work without creating a claim. External senders can still consume capacity between that observation and a request. Provider quota refusal stops the run; no guaranteed Auth reservation is claimed.
An owner test requires the owner's explicit confirmed subscription, a usable unsubscribe link and a receipt note with measured or explicitly attested unsubscribe evidence before the matching campaign can run. Acceptance by the provider is not inbox receipt. The campaign rechecks subscriber eligibility before each dispatch. If fresh consent changes a previous campaign payload, that recipient is reported as conflicted without changing the old intent or sending again; unrelated recipients continue. The operator guide is mail/README.md.
Before/after evidence. Earlier immutable candidates reproduced duplicate-key retry, stale-claim, accounting and token-lifecycle failures. The final integrated local gates use the shipped migrations rather than substitute SQL: SQL 173/0, mail flow 60/0, sender fault scenarios 27 passed, lifecycle 112/0, account-usage unit tests 10/0, and usage/sender integration 8/0. Counts are checks within their suites, not distinct defects. Provider replies are modeled; no real mail was sent. An intermediate same-claim snapshot failed two root expectations; the final sender retains uncertainty and the integration regression passes. A worker mutation removing that rule made the mail test fail as intended.
Before activation (4 October). A review against Resend's and Supabase's own documentation found gaps that local tests could not show. A sending-only Resend key is refused by the usage endpoint (401 restricted_api_key): the worker used to defer every run with "account usage unavailable" and no cause; it now names the status and error and says a full-access key is needed, without echoing the key or the response text. After a 429 rate-limit refusal the sender now waits before its next derived key (the provider's retry-after when sane, else 1 s then 3 s, at most 60 s); before, all three keys were spent within milliseconds and that recipient missed the issue. The limit is per team and shared with dewsletter. Re-applying 002 no longer deletes a consumer row the operator configured, and the ledger's id sequence is revoked from anon and authenticated, which existing Supabase projects grant on every new public sequence.
Two operator scripts prepare the first live apply. mail/ops/preflight_readonly.sql reports the shared project in one read-only transaction: dewfpga objects and grants, other applications' tables tied to auth.users, triggers, default privileges, foreign callers and which migration parts already exist. mail/ops/rollback_dewfpga.sql removes only dewfpga's objects and drops nothing when another application uses them. Both live outside mail/sql: in the first draft a loop over that folder ran the rollback right after the migrations, which a sender test caught. Local gates on the integrated head: activation scripts 142/0, SQL 187/0, mail flow 60/0, every sender scenario including the 429 wait, lifecycle 112/0, usage 13 tests OK, pages 137/0, Chromium and WebKit 158/0. Provider and Auth replies are modeled; nothing was applied to the live project and no mail was sent.
#26 · Optional accounts with local session handling and scoped deletion · 3 October
The account page (not open yet) uses Supabase magic-link signup/sign-in and explicit dewfpga enrolment. Premium is marked coming soon; existing free features remain available without membership. Export and deletion apply to the dewfpga profile and token-linked subscriptions. They preserve the shared Auth identity and other applications' records. Anonymous subscriptions remain managed by their own links. The application installs no shared Auth signup trigger.
Forms ship disabled until their handlers and configuration are ready. Malformed Auth fragments are removed without breaking the page; blocked storage and expired sessions produce explicit feedback. Temporary service failures preserve the session for retry. Logout requests use local scope. A failed logout distinguishes local clearing from unconfirmed server revocation. A lost response to a write says its result is unknown, rather than falsely claiming no change occurred.
Validation. On the integrated schema, real Chromium and WebKit ran 79 checks each, 158/0 total, against private PostgreSQL, a PostgREST-shaped test adapter and fake Auth. This includes disabled/no-JS forms, malformed links, consent, account controls, expired sessions and a real SQL write followed by a dropped HTTP response. Source/helper checks passed 137/0. The private database now shuts down cleanly after browser tests. CI adds a separate offline-mail gate and the Pages job depends on it; no CI step enables live sending.
Activation and closure are still open. Supabase configuration, sender domain, SMTP delivery, real owner receipt/unsubscribe, final notice details and a live announcement have not been verified or performed. This combined candidate's SDK-backed full CLI run passed 273/0 with 52 known gaps in 17 minutes 35 seconds; tested source hashes stayed unchanged. Pages and mirror bundles each passed 109 pages, 2006 links, 0 failures; the CLI archive remains byte-identical to the previously accepted release. A separate Opus review exercised the consent-conflict path and found no demonstrated safety fault within its stated inspection scope. It exposed the conservative limitation that even a proven refusal is not automatically reissued for fresh consent; that policy and per-run log are now explicit, with all four prior outcomes covered. Exact-head remote CI remains a required PR gate. No live publication, main-branch merge or physical-board/GUI acceptance is implied.
#28 · dewfpga sim simulates what the build reads when iverilog refuses the design · 4 October
The simulation no longer stops where the bitstream goes through. Of the 111 probes Vivado supports, 52 did not pass all four stages, and 28 of those failed only in dewfpga sim: iverilog does not read unique if, interfaces and modports, unpacked structs and unions, streaming, whole-array compare/copy/slice, a class used for its static function, the type operator and more, while the build read the same file and made a working bitstream. A student got a .bit from dewfpga bit and design.sv:7: ERROR [iverilog-refused]: iverilog cannot compile this code from dewfpga sim.
Now, when iverilog refuses a design file (not the testbench), sim runs the build's own front half, the same readers and the same checks as dewfpga bit, writes that design back as Verilog (top_sim.elab) and simulates it with the testbench and yosys' models of the Xilinx cells: design.sv:7: note [sim-from-build]: iverilog cannot compile this design (its lines are above), so the simulation runs on the design as the build reads it ..., then the testbench's own PASS. A design the build refuses is refused by sim too, with the build's line: a function with a ref argument, which yosys-slang silently drops, still stops instead of passing (measured: without that rule the second path printed the testbench's FAIL). A testbench iverilog cannot read gets the old error. What differs from simulating the source (delays inside the design are ignored, a testbench reference to an internal signal the build renamed can break) is on the new error page.
An independent review found one case this path got wrong: a testbench that sets the top module's parameters (top #(.W(2)) dut). The build keeps the defaults, so the elaborated design had no parameter left and iverilog blamed the student's testbench. Vivado's simulator applies those values; this path cannot, so it now stops at the testbench's line with [sim-from-build-parameters] and says why, instead of testing another design.
Numbers. Vivado-supported probes passing all four stages: 59/111 → 86/111; all four stages 64/133 → 95/133; synthesis 108/133 → 108/133. Known gaps: 52 → 25. Silent wrongs (bitstream built, netlist check fails): 3 → 3, the same rows (35, 74, 97). Closed in simulation: 31 probes (27 of them Vivado-supported). Not closed: 35 (no simulation model of MMCME2_BASE in yosys' cell library), 51c (the 2-D typedef scan stops before iverilog), 60 (a ref argument, refused on purpose). Blink's simulation is byte-identical and as fast as before; a design on the second path takes about 3 s instead of failing.
Tests. 6 new checks; each turns red when its rule is removed: the second path passes an interface design; a ref design is refused, not passed; a testbench iverilog cannot read keeps the old error; a design both readers refuse prints both; a testbench that sets parameters gets the new error at its line; blink's output is unchanged.
#29 · A failed build leaves no old bitstream, wrong pins are named, a quick save rebuilds · 4 October
A wrong pin name stops at its own XDC line. With PACKAGE_PIN ZZ99 on the clock line, the pin check printed xdc ok: 33 ports, all mapped., then nextpnr stopped with its raw ERROR: Unable to constrain IO 'clk', device does not have a pin named 'ZZ99', no file, no line, no fix. Now the check reads the chip's own pin list (the prjxray database the bitstream is built from) and stops first: blink.xdc:5: ERROR [no-such-pin]: ZZ99 is not an I/O pin of the Basys3's chip ... Fix: .... A pin written in small letters (w5) and two ports on one pin ([pin-used-twice], both lines named) are caught the same way. If nextpnr still refuses a pin, its line gets the same code and XDC line.
A failed build leaves no old bitstream. After that failure the previous blink.bit was still in the folder, byte for byte the old design, with its .fasm, .frames and routed netlist. Anything that loads the file by name would load yesterday's design. The pin-and-route stage now removes its outputs before it starts, as synthesis already did; dewfpga flash after a failed build stops at the same error line.
A save in the same second rebuilds. macOS's make (3.81) compares whole seconds. A testbench edited within the second of the last compile was not newer to it: dewfpga sim ran the old simulation and printed its old PASS (measured: an edit 0.4 s after the build, 0 of 5 rebuilt). VS Code's save and build in one keystroke hits exactly this. The stage records a checksum of every source next to its file list; when the content differs the stage builds again. With the planted $error and the same timestamp, sim now compiles, prints the error and exits 2. Blink's simulation time did not change (0.14-0.20 s).
On a real Basys3 (6 October). The board test ran with a board for the first time: dewfpga flash programmed blink (openFPGALoader's Done line, no ERROR). With PACKAGE_PIN ZZ99, dewfpga flash stopped at blink.xdc:5: ERROR [no-such-pin], never started the programmer, and left no .bit.
Tests. 8 new checks (listed checks 329 → 337; with #28's six, 343), each with a mutation: removing the old-output cleanup, the pin list, the duplicate-pin check, nextpnr's coded lines or the checksum rebuild turns its check red. The same-second checks ran three times in a row and passed each time. Not verified: nextpnr's behaviour on the chip's 14 transceiver pads, which the pin list accepts.
Closing run (6 October, board unplugged). test/run.sh: passed 314, failed 0, known gaps 25 in 21 min 7 s, the board check skipped (SKIP flash on the board: no board; no FTDI device on the USB bus at that hour), the probe table byte-identical to the two runs before it. Two checks were red in the run before it, neither in the product: shellcheck read the Verilog $error in a new test function as a shell variable (the directive the sibling functions carry was missing), and the MCP check's SDK venv lived in $TMPDIR, which macOS empties of files untouched for three days (the venv was built on 3 October; on the 6th it had 420 directories and 0 .py files, so dewfpga mcp reported mcp-not-installed; python3.14 -m venv and pip install -r templates/mcp-requirements.txt rebuilt it and the check passed alone and in the full run). The mail suites: 9 suites, each exit 0 (activation SQL 142, SQL 187 with 154 privilege checks, mail flow 60, sender idempotency, lifecycle 112, usage 13, integration 8, pages 139, Chromium and WebKit 158; 0 failures). Site check: 112 pages, 2053 links, 0 failures.
#30 · The install keeps only the chain that runs: about 0.25 GB on disk instead of 1.5 · 7 October
What a clean install left behind. The install line said # ~4 min, 1.4 GB, once, and a clean install into an empty folder measured on 7 October was 1543 MB with 25 .git folders in it: nextpnr-xilinx 1086 MB (its full prjxray-db submodule holds five FPGA families, 853 MB, the Basys3 reads one), prjxray 217 MB (ten submodules, four never built), yosys-slang 101 MB of sources around a 9 MB plugin, and every build tree. The chain that runs, the binary, the artix7 database, the chip database, the venv, fasm2frames, xc7frames2bit and slang.so, is 237 MB. The installer asked for 4 GB free: the install needs 1.4 GB plus build scratch, at least 4 GB.
Now the install fetches less and removes what it built from. nextpnr-xilinx comes with the two submodules its chip-database exporter reads, prjxray-db as a partial clone of artix7 alone; prjxray with the six submodules its cmake uses; the four it never builds (fasm, yosys, python-sdf-timing, display_port, 100 MB) are not fetched. After each build the sources and the build tree go, and the installer says what stays: ✓ prjxray pruned to xc7frames2bit, utils/ and the python package (2 MB), ✓ nextpnr-xilinx pruned to the binary and the artix7 database (86 MB), ✓ slang.so built, loads in Yosys 0.69+post (read_slang); sources removed (9 MB kept). Every step header carries the size so far ([6] yosys-slang (SystemVerilog reader plugin, from source, ~3 min) (+218s, 246 MB)), the last line is Total: 415 s, ~/fpga: 255 MB., and the free-space check now asks for 2 GB: the build peaks at about 850 MB (the install that stays is ~237 MB), so at least 2 GB are needed. (the peak printed by the three clean installs of 7 October: 811, 833 and 834 MB, at the chip database step, when the sources, the build trees and the 268 MB .bba are all there at once). A second run skips every step by its stamp and the files it left, no git involved: ↷ nextpnr-xilinx @ 3fd7878 (binary + artix7 database) (already there, skipped), `↷ prjxray @ c9f02d8 (xc7frames2bit
- fasm2frames) (already there, skipped)
,↷ slang.so @ 9676786 (built for Yosys 0.69+post) (already there, skipped),Total: 3 s. A chip database that went missing with the sources already pruned fetches and builds nextpnr-xilinx again, and prunes it again. An install interrupted during a build (Ctrl-C, sleep, a compiler error) is resumed by the next run with the checkout still there, and that run prunes it like a first run would (found by the independent review: the first version only pruned what the same run had fetched, so an interrupted install kept 637 MB and three.gitfor good). Aslang.sobuilt by hand with the manual's lines, which never write the installer's commit stamp, is load-tested, stamped and pinned instead of being cloned and compiled again (130 MB, 3 minutes); a hand checkout at another commit is named (the checkout at ~/fpga/yosys-slang is at, not the pinned 9676786 ) and the pinned plugin is built next to it. A Yosys upgrade still rebuilds the plugin in a side folder and replacesslang.soonly once Yosys loads the new one. The manual's new section 3.7 lists what a by-hand chain can delete to land at the same size. The pages say the measured number (about 0.3 GB on disk,237 MB measured on 7 October), and that an install made before 7 October keeps its 1.5 GB untildewfpga uninstall && dewfpga install`.
Numbers. Clean install into an empty folder: 1543 MB → 237 MB in the closing run's second full run (the suite's du right after the install, log included; the first full run's du said 253 MB, and the integrator's install of the same morning measures 237 MB), .git folders 25 → 0, 415 s for the install, 3 s for the second run. The Homebrew side, which the pages' number never included, measured on this Mac: the eight packages the installer asks for are 237 MB (python@3.14 87, cmake 71, yosys 50, eigen 11, openFPGALoader 10, Icarus Verilog 7, ninja and pkg-config under 1), and the 16 packages they pull in another 562 MB (boost 377, icu4c 84, tcl-tk 43, openssl 39). So a student's Mac gets about 0.25 GB in ~/fpga and about 0.8 GB under Homebrew, plus the 51 MB agent SDK if they ask for it. The compiler did not change: the probe table of the closing run is byte-identical to #28-#29's (synthesis 108/133, all four stages 95/133, Vivado-supported probes passing all four stages 86/111, known gaps 25), and the fresh chain's blink .fasm equals the golden one.
Tests. Four new checks in the FULL=1 section (listed checks 343 → 347): the clean install is under 1 GB (its MB printed under the PASS line), it leaves no .git at any depth, a second run skips every step in under 10 s with no built line, and uninstall removes the fresh folder whole. The size checks were run by the update's builder against the installer from before this update and fail there (1543 MB, 25 clones); against the new one they pass. The suite now also puts the tree's .fpga_home back after the clean install, which the install's step 7 overwrites. shellcheck -S style and bash -n on the new install.sh are clean. Not exercised end to end: the rebuild after a Yosys upgrade (same code path as the clean build, with the pruning after it) and a venv rebuilt after a Homebrew Python upgrade (prjxray keeps setup.py and README.md for that pip install -e).
Closing run (7 October, board unplugged). The first run: passed 319, failed 1, known gaps 25, and the one FAIL was not the product: no personal paths found templates/__pycache__/mcp_setup.cpython-314.pyc, left at 04:41 by a run of the MCP test without -B (its rollback test imports mcp_setup.py), carrying this Mac's home path, the same class of leftover #7's closing run hit. The test now sets sys.dont_write_bytecode like its two sibling suites, so a run without -B leaves nothing. The second run: passed 320, failed 0, known gaps 25, one PASS more than the first (the fixed check), its probe table byte-identical to the first run's and to #28-#29's. The clean install took 415 s in the first run (peak 811 MB) and printed a peak of 810 MB in the second; its second run took 3 s both times, and find -name .git over the live fresh folder the moment its last build ended found nothing (89 MB chipdb, 86 nextpnr-xilinx, 52 venv, 9 yosys-slang, 2 prjxray, 1 install.log: 237 MB). The clean install's du is the number above. The board check: SKIP flash on the board: no board (no FTDI device on the USB bus during either run). Mail suites: 9 suites, each exit 0 (activation SQL 142, SQL 187, mail flow 60, sender idempotency, lifecycle 112, usage 13, integration 8, pages 139, Chromium and WebKit 158; 0 failures). Site check: 112 pages, 2055 links, 0 failures.
Open.
- An install from before 7 October keeps its clones (1.5 GB); the installer leaves it as it is, and the pages say the two commands that get the small one.
- The 51 MB for the optional agent SDK is the figure from before this update, not re-measured today (no
mcp-venvon this Mac's~/fpga). - The
.bitof two installs differs by 2 bytes, the timestamp in its header; the frames are identical. - On the skip path the installer trusts the stamps and does not load
slang.sointo Yosys again (dewfpga checkdoes, every time it runs); a new pinned commit or a newyosys -Vline still rebuilds. - The rebuild's sources go to
~/fpga/yosys-slang/src.new, inside the folderuninstallremoves, so a rebuild that failed leaves nothinguninstallwould miss. - Every
install.shrun links Homebrew'sbin/dewfpgato the CLI that ran it and writes that tree's.fpga_home; the suite restores.fpga_home, the link is restored by hand after a run from a checkout. - The three pruned checkouts'
.gitsat at depth 2, 4 and 4; the no-clone check looked two levels deep when the review read it and now looks at every depth (verified withfindon both installs of 7 October: 0 and 25).
#31 · A random-design generator checks the SystemVerilog DDCA teaches against its own simulation · 7 October
The product did not change in this update. It adds a test tool, test/fuzz, separate from test/run.sh: gen.py <seed> <folder> writes a random design in the SystemVerilog the course book (DDCA, chapters 4, 5 and 7) and CS223 teach, on the Basys3's own ports, with its XDC and a testbench of 300 cycles of random switches and buttons that prints every output every cycle and expects nothing. Forty language features are switches (always_comb, case without default, casez, unique case, async reset, a start value in the declaration, always_latch, an enum state machine, parameters and #(.N(..)) overrides, generate for/if, packed and unpacked arrays, a memory, packed structs, typedefs, functions with break, a for loop in always_comb, named, positional and .* connections, a package in its own file, submodules in their own files, signed arithmetic, shifts, reductions, concatenation, $clog2, mixed widths, the seven-segment ports, three or four levels of modules, the same submodule twice); a seed draws them, --on/--off force them, so a failing design narrows to the feature that causes it. run.sh <first seed> <count> runs each design through dewfpga sim (the RTL in iverilog) and dewfpga bit, then simulates the netlist the build made (read_json, write_verilog, iverilog with yosys' Xilinx cell models, the same testbench, the way test/sv does) and compares the two traces bit by bit: a bit the RTL knows (0 or 1) must be the same in the netlist, an x in the RTL matches anything. Six outcomes per seed: ok, silent-wrong (the bitstream built, the netlist does not do what the simulation showed), refused-coded, refused-uncoded, crash, sim-refused (no independent reference: dewfpga sim failed or ran on the build's view). The last line is the metric, fuzz: ok 123 of 144; silent-wrong 10; refused-coded 6; refused-uncoded 0; crash 0; sim-refused 5, and a failed seed's folder stays.
What 194 designs found. Seeds 1-12, 100-243 and 500-537 (two batches stopped at the 90-minute mark; a seed costs about 8 s, of which dewfpga bit about 4.5 s): 168 ok, 14 silent-wrong, 6 refused with a code, 0 refused without one, 0 crashes, 6 sim-refused. The failures fall into six families, each with the update that owns it:
- Silent wrong, new:
unique casezwith overlapping items (4'b10??and4'b??11, switches1011): the simulation takes the first item, the netlist ORs both. Shrunk to eight lines;rtl: 0000000000000001 net: 0000000000000011. Seed 6. - Silent wrong, new:
always_latchwhose enable and data come from the same switches: the netlist's latch captures a transient value, the RTL does not (a hazard of the latch; the board would glitch the same way). Shrunk to six lines. The product warns about a latch inferred fromalways_comband says nothing aboutalways_latch. Seed 7. - Refused with a code, #33 (readers): a
.*connection to a submodule whose parameter override changes a port's width. yosys' own reader refuses it first, then yosys-slang:top.sv:224: ERROR [slang-refused]: implicit named port 'u_m0_in0' of type 'logic[11:0]' connects to value of inequivalent type 'logic[4:0]'. Six seeds (106, 129, 163, 179, 189, 196). Whether this is the student's error or the reader's needs a Vivado check. - Sim refused, #33 (readers), the known gap
23c: a package in its own file imported without`include:top.sv:7: ERROR [package-file-not-given]:import fuzz_pkg::*names the package fuzz_pkg, which is declared in fuzz_pkg.sv, and the tools are not given that file, fromsimand frombit. Six seeds (9, 168, 185, 186, 192, 200). The generator follows the product's convention (`includein 9 designs of 10); the plain Vivado way, the package file just in the folder, is the gap. - Silent wrong, #34 (models and timing), not shrunk: the netlist prints
xbefore the first clock edge where the RTL prints 0 or 1 (a state register at the cycle-0 line):rtl: 0000000000000000 net: 000000000000xxxx. Six seeds (134, 151, 157, 161, 162, 195). Not verified whether the board or yosys'cells_sim.vis what differs. - Silent wrong, new, not shrunk: known bits differ after the start (cycles 1 to 131) and the cause is not found. Six seeds (121, 125, 159, 214, 512, 526); each regenerates from its seed. Two more came up while shrinking and while bringing the generator up, both for #33 (iverilog is the RTL reader): iverilog 13 refuses an enum next state written as a ternary (
next = sw[0] ? S1 : S0:top.sv:7: error: This assignment requires an explicit cast.), sodewfpga simruns on the build's view and the check has no independent reference; andbreakinside a for loop inalways_combmakes iverilog 13 abort with an internal error (the product falls back to sim-from-build).
Numbers. From the closing run of this tree: Vivado-supported probes passing all four stages 86/111 → 86/111, all four stages 95/133 → 95/133, synthesis 108/133 → 108/133, known gaps 25 → 25, the probe table byte-identical to #30's closing run (the compiler did not change). test/run.sh: passed 314, failed 0, known gaps 25 in 20 min 52 s, the board check skipped (SKIP flash on the board: no board; no FTDI device on the USB bus at that hour), no check changed since #30 (the 320 of #30's run included its FULL=1 clean install, not run here). The listed count stays 347: test/fuzz is not in the suite and is not in CI.
Tests. The tool is the test; what it checks on the product is the comparison above. The four shrunk reproductions were run through run.sh's steps by hand: the unique casez and the always_latch ones MISMATCH, the enum state machine and an 8x8 multiplier EQUAL: 4 cycles. compare.py on the kept folders gives the lines quoted above. Mail suites: 9 suites, each exit 0 (activation SQL 142, SQL 187, mail flow 60, sender idempotency, lifecycle 112, usage, integration, pages 139, Chromium and WebKit 158; 0 failures). Site check: 112 pages, 2056 links, 0 failures.
Open.
- 194 designs ran, not the 300 asked: the two background batches were stopped at the 90-minute mark (seed 243 of 100-499, seed 537 of 500-699).
JOBS=3 test/fuzz/run.sh 100 300covers 300 seeds in about 15 minutes on its own. - The two unshrunk families (netlist
xbefore the first edge; known bits differing after the start) are counted, not explained. Their seeds regenerate the designs. - No Vivado ran anywhere: the
.*family's blame (student or reader) and thealways_latchfamily's board behaviour are not checked against it. - The comparison is x-tolerant on the RTL side, so a difference hidden under an uninitialised memory read in the RTL is not seen; a netlist
xwhere the RTL knows the bit is counted. - iverilog prints
sorry: constant selects in always_* processes are not fully supportedon most designs (17 of the 24 kept folders of the second batch); the simulations ran, and the warning's effect on them is not checked. - Not generated: a module declared inside another (hierarchy depth, 1 to 4 levels over several files, stands in for it),
$readmemhmemories, interfaces, and nothing produced a timing failure. - The two silent wrongs that are understood (
unique casez,always_latch) are reported here and not fixed: the product's warnings for them are a later update's work.
#32 · A latch whose gate and data change together, and a unique casez whose items overlap: the chain names both before the board differs from the simulation · 7 October
The two silent wrongs #31's generator found and left open are now named by the product. Before, a student with an eight-line unique casez whose items 4'b10?? and 4'b??11 both match 1011 typed dewfpga sim and saw led = 1 for that input, then dewfpga bit and saw the build go through as if nothing were wrong:
xdc ok: 32 ports, all mapped, 73 unused pins in the XDC ignored.
pnr ok: 2 LUT, 0 FF, no clocked paths, timing not applicable (full log: top.log)
top.bit 2.2 MB
and the board showed led = 3: iverilog ignores unique and takes the first item; yosys, yosys-slang and Vivado (UG901 v2023.2 Ch.10 p.279: a unique case is parallel_case and full_case) build the items as parallel logic and OR their values when two match. Now the source scan that runs before the tools stops both dewfpga sim and dewfpga bit the same way, with the two items, the value they share and the fix on the line:
design.sv:5: ERROR [unique-case-overlap]: the items 4'b10?? (design.sv:4) and 4'b??11 (design.sv:5) of this unique casez both match 4'b1011, and unique promises that only one item can match: the simulation takes the first item (iverilog ignores unique), and the hardware, built as parallel logic the way Vivado builds a unique case (UG901 Ch.10, parallel_case), ORs the two items' values, so the board would not do what the simulation showed. Fix: make the items disjoint (4'b10?? and 4'b0?11), write priority casez when the first match should win, or drop unique. https://nosey-dewdrop.github.io/dewfpga/errors/unique-case-overlap/
The rule: a unique or unique0 case, casez or casex whose every label is an integer literal, or a localparam of the module whose value is one, is checked pairwise for a value two items both match (a ? or z, and x in a casex, is a wildcard); a statement with an enum name, a parameter (an instance may override it), an expression, a range or inside among its labels is skipped, no guess. Two labels of one item (4'b10??, 4'b??11: led = 1;) are one item with one value and may overlap. A localparam item is named with its value (HI = 4'b10zz) so the overlapping bits are on the line. The same unique case with disjoint items, as the course's 18_unique_priority writes it, builds as before.
The second one, an always_latch whose gate and data come from the same switches (seed 7 of #31, cut to six lines: always_latch if (s < sw) led = {15'b0, (sw < 16'd1791)}; with s a function of sw), built before with pnr ok: 22 LUT, 1 FF, no clocked paths, timing not applicable and not a word, and the simulation kept led = 0 where the netlist, and the board, take led = 1: a latch has no clock edge to order its inputs, the gate and the data arrive through LUTs of different delay. The latch is what the student asked for and both readers build it right (one LDCE, as Vivado does, UG901 Ch.5), so the build goes on, and now warns once per always_latch, with its gate:
design.sv:4: warning [latch-hazard]: always_latch builds a latch for led (an LDCE cell, as Vivado builds it, UG901 Ch.5 Latches), and a latch has no clock: when its gate (s < sw) closes in the same instant its data changes, the board keeps the old value or takes the new one depending on which wire is faster, where the simulation always keeps the old one; dewfpga sim cannot show that race. Fix: if a register was meant, write always_ff @(posedge clk) with the gate as its enable: if (s < sw) led <= ...; keep always_latch only when the gate is held steady while the data changes. https://nosey-dewdrop.github.io/dewfpga/errors/latch-hazard/
The warning reads the $dlatch cells after proc (yosys' own Latch inferred line is hidden for an always_latch, yosys-slang prints none) and names the first if ( of the block; comments are blanked before any rule reads a source line, so always_latch // if (reset) was here once names the if on the next line, and an always_comb whose comment says always_latch still gets [latch], not [latch-hazard]. Both messages have their page in the catalog (errors/unique-case-overlap/, errors/latch-hazard/), with the design that printed them and why.
test/fuzz/run.sh learns the difference: a seed whose netlist differs from its RTL in a known bit (0 or 1 on both sides, not an x) while the build printed warning [latch-hazard] is warned, not silent-wrong: the product named, with the line, the construct whose board behaviour differs from the simulation. A netlist x where the RTL knows the bit is never excused by a latch warning. A coded refusal at the sim step is now refused-coded, not sim-refused: the scan stops sim and bit the same way, so it is the product's refusal, not a missing reference. And the line before the metric splits the refusals by code (the line quoted under Numbers), so a check that moves designs out of ok is weighed by name.
Numbers. From the closing run of this tree: Vivado-supported probes passing all four stages 86/111 → 88/113, all four stages 95/133 → 97/137, synthesis 108/133 → 111/137, known gaps 25 → 26; the 133 rows of #31's table are byte-identical, the four new rows are the four probes below. The new gap is 101c_always_latch_race, a documented silent wrong: the bitstream builds with the warning, the netlist's LDCE takes the new data as the gate closes (FAIL: step 1: sw=0000 led=0001, expected 0000) where the RTL keeps the old. test/run.sh: passed 317, failed 0, known gaps 26 in 18 min 46 s (the second run; its probe table equals the first run's byte for byte). The listed count goes 347 → 351 (the four probes); the first closing run was passed 316, failed 1, the one failure the count lock (README, README.tr and site/cli still quoted 347 checks and 133 probes), fixed in the close. The board check was skipped (SKIP flash on the board: no board; no FTDI device on the USB bus).
test/fuzz/run.sh 1 500, the same 500 seeds as #31's second measurement, before this update: fuzz: ok 438 of 500; silent-wrong 27; refused-coded 21; refused-uncoded 0; crash 0; sim-refused 14. After, in the close:
fuzz: refused-coded by code: unique-case-overlap 24; slang-refused 20; package-file-not-given 14
fuzz: ok 416 of 500; silent-wrong 19; warned 7; refused-coded 58; refused-uncoded 0; crash 0; sim-refused 0
(JOBS=3, 17 min 53 s.) Seed by seed against the before run: 416 stay ok; 22 go ok → refused-coded (27, 83, 128, 170, 174, 182, 197, 275, 289, 290, 297, 305, 322, 336, 360, 362, 363, 406, 443, 485, 493, 497: a unique casez with overlapping items whose netlist nonetheless equalled the RTL, see Open); 1 silent-wrong → refused (seed 6, the overlap that built wrong); 7 silent-wrong → warned (7, 88, 125, 159, 327, 384, 412: each has an always_latch, the build printed [latch-hazard], the first difference is a known bit); 19 silent-wrong stay silent-wrong (the netlist-x-at-cycle-0 family and the unexplained one, 121 and 214 among them); 14 sim-refused → refused-coded by the counting rule alone (the package-file-not-given family, the same refusal now counted as the product's); the 21 refused stay refused. No seed went from ok to silent-wrong, no crash. Read plainly: the two families this update names are 8 of the 27 silent wrongs (7 warned, 1 refused), 19 remain unnamed, and ok drops by 22 designs that were right by accident.
Tests. Four probes with their expect.tsv rows, each quoted message checked against the run's output by the probe runner (a probe that stops matching expect.tsv fails the suite): 100_unique_casez_overlap (rtl fail, synth fail, bit fail: no top.json, no top.bit, the error from sim and from bit), 101_always_latch (a latch on purpose, read by yosys' own reader: builds, one warning at the always_latch line, 0 LUT, 1 FF), 101b_always_latch_slang (the same latch next to a foreach, so yosys-slang reads it: the same warning, note [read-with-slang]), 101c_always_latch_race (the gap above). The eleven neighbours of the two rules (01_latch_comb, 18_unique_priority, 18b, 18c, 19_casez_casex, 10_enum_fsm, 43_foreach_loop and the four) matches expect.tsv: 11/11; 16 of the 95 probes the integrator listed as the rules' blast radius ran through test/sv/run.sh after the change, and the old and the new scanner were run with --scan on all 134 probe folders for bit and for sim (268 scans): byte-identical output everywhere but 100_unique_casez_overlap. The judge's own run of 100, 101 and 101c: matches expect.tsv: 3/3. No new check went into test/run.sh beyond the probes: the fuzz runner's warned rule and its by-code line have no unit test. Mail suites: 9 suites, each exit 0 (activation SQL 142, SQL 187, mail flow 60, sender idempotency, lifecycle 112, usage, integration, pages 139, Chromium and WebKit 158; 0 failures). Site check: 114 pages, 2092 links, 0 failures.
Open.
- The overlap scan reads the patterns, not the values the items assign, so a
unique casezwhose overlapping items happen to agree (seed 27: the OR of the two values is the first item's value) is refused with the same words, "the board would not do what the simulation showed", though for that design it would: in the 500 seeds 22 of the 24 refused overlaps built equal to their RTL before the scan existed, 1 (seed 6) built wrong, 1 (seed 345) was refused by yosys-slang first. The design is still wrong by IEEE 1800-2017 12.5.3; the message's claim is wider than what was measured for those 22. warnedexcuses a known-bit mismatch of any design that has analways_latchand printed the warning, not only a mismatch at the latch: a design with a latch and an unrelated difference counts as warned. Round 3 narrowed it to known bits (seed 157, a netlistxon a register at cycle 0 in a design that also has a latch, went back tosilent-wrong); the attribution to the latch's own signals is not checked.- An
always_latchgated by acase, with noif, warns "when its gate closes" without naming the gate, and its Fix readsif (gate) q <= .... - The browser mirror of the source scan (
sim/src/sim/source-scan.js, which carriesdecl-init-reads-signal) does not carryunique-case-overlap: the browser build of an overlappingunique casezgoes through. - No Vivado ran: the
100row's vivado column isunverified(UG901 says unique case is parallel_case and does not say what Vivado does when two items match), and the101crow's board behaviour (which wire is faster) is argued from the LDCE, not seen on a board. The Basys3 was not on the bus during this close. - Labels the scan does not read (enum names,
parameters, expressions, ranges,inside) are skipped without a word: an overlap written with them is not caught. - The two unexplained silent-wrong families of #31 (netlist
xbefore the first edge; known bits differing after the start, seeds 121 and 214) are untouched; #34 owns the first.
#33 · The chain reads every design file, not only the ones that hold a module, and a .* connection whose port and signal widths differ is named with the fix before either reader refuses it · 7 October
Two families #31's generator left to this update. The first is the oldest gap in the probe table, 23c_package_own_file (since #4): a lab folder with pkg.sv (package cfg_pkg; localparam logic [3:0] MAGIC = 4'hA; ...) next to a design.sv that writes cfg_pkg::MAGIC. Before, the CLI handed the tools only the files that hold a module, so pkg.sv was never read, and dewfpga sim and dewfpga bit both stopped before the tools with
design.sv:2: ERROR [package-file-not-given]: cfg_pkg::MAGIC names the package cfg_pkg, which is declared in pkg.sv, and the tools are not given that file (dewfpga hands them the files that hold a module: design.sv): iverilog stops at a syntax error there; yosys would drop the import and build every name of cfg_pkg as an undriven 1-bit wire: x on the board. Fix: add `include "pkg.sv" as the first line of design.sv; or move the package into design.sv, above the module. https://nosey-dewdrop.github.io/dewfpga/errors/package-file-not-given/
and the README said "Standalone package/interface/typedef-only files remain a CLI limitation". Now the same folder simulates and builds without a line added to it:
top module: top
iverilog -g2012 -o top_sim pkg.sv design.sv tb.sv
PASS
tb.sv:11: $finish called at 256 (1s)
xdc ok: 32 ports, all mapped, 73 unused pins in the XDC ignored.
pnr ok: 2 LUT, 0 FF, no clocked paths, timing not applicable (full log: top.log)
top.bit 2.2 MB
The rule: every .sv and .v the CLI collected goes to the tools, the files that hold no module first (a package, an interface, typedefs at file scope: Vivado lists such files among a project's sources, UG901 Ch.10 Compilation Units and Packages), each after the files that declare a name it uses: a package before the files that name it (z_pkg.sv before a_pkg.sv when a_pkg imports z_pkg, whatever the alphabet says), a typedef file before the interface that uses the type; among the ready files a package file comes first, then the order given; the module files follow. A file a design source includes is left to the include (handing it too declares the package twice, and iverilog stops at its first line). A file the testbench `includes is not taken out of the build's list, since the build never reads the testbench; the simulation, which reads both, refuses the second copy with the line to delete:
tb.sv:1: ERROR [included-twice]: tb.sv `includes pkg.sv, and the simulator reads pkg.sv with the design already (dewfpga hands every .sv and .v of the folder to the simulator, a package file first: pkg.sv design.sv): read a second time, package cfg_pkg is declared twice and iverilog stops at its first line ('syntax error', 'I give up'). Fix: delete the `include "pkg.sv" line from tb.sv; cfg_pkg reaches the testbench through the design, which iverilog reads first. https://nosey-dewdrop.github.io/dewfpga/errors/included-twice/
yosys-slang, the second reader, now reads the files as one compilation unit (--single-unit), as yosys' own reader and Vivado (UG901 Ch.10 Compilation Units, p.269-271) do: a typedef at file scope in types.sv is visible in iface.sv. When slang still reports a name that some file declares as a typedef, [undeclared-name] names that file and whether it is read after the user or never (a .svh no file includes), with the include fix. The [package-file-not-given] line stays for the case that remains: a package in a .svh or .vh no design file includes, or in a file outside the folder or the project; it now says which files the tools are read ("every .sv and .v of the folder or of the project: design.sv; a .svh or .vh reaches them only through an include"). With this, 22b_interface_own_file (an interface in sum_if.sv) and 23c build and run on the board's netlist, and 56b_unit_typedef_file (a typedef-only types.sv) simulates and builds (see Open for its netlist half).
The second family: a .* connection whose port and signal widths differ (six seeds of #31, probe 102_dotstar_param_width: sub #(.N(8)) u (.*); with logic [3:0] sub_in, sub_out;). Before, dewfpga sim printed iverilog's two warnings (design.sv:7: warning: Port 1 (sub_in) of module sub expects 8 bit(s), given 4., Padding 4 high bits of the port.) and PASS, and dewfpga bit stopped with yosys' own line and two [slang-refused] lines with a caret under .*, the message of a reader, not a verdict. Now both commands print one line of the product's, the same at sim and at bit, with the instance, the port, the two widths, what the simulation did with the difference, and the fix:
design.sv:7: ERROR [implicit-port-width]: the .* connection of u (module sub) joins the port sub_out, 8 bits wide with N = 8, to the signal sub_out declared 4 bits wide: an implicit connection (.* or .sub_out) takes the port and the signal exactly as declared, with no padding or cutting (IEEE 1800-2017 23.3.2.3), so neither reader builds it, where dewfpga sim (iverilog) widened the signal to the port, so the port's 4 high bits reach nothing (its line: Padding 4 high bits of the port) and ran. Fix: declare sub_out with the port's width (logic [7:0] sub_out;), or connect it by name, .sub_out(sub_out), which pads or cuts to the port's width as the simulation did. https://nosey-dewdrop.github.io/dewfpga/errors/implicit-port-width/
The simulation finds the case in iverilog's own padding warning and refuses with the build's line, so a student never sees a simulation pass that no bitstream can be built from. What the simulation did is said per port direction, the way iverilog's second line says it (an input port cuts a wider signal or pads a narrower one; an output port wider than the signal widens the signal, so the port's high bits reach nothing). When the mismatched signal is a port of the parent module itself (.* to an 8-bit led on sub inside top(... output logic [15:0] led)), redeclaring it is no fix (its width is the board's wiring), and the message gives the named form instead, .led(led[7:0]). The check reads the design's files only: the same mismatch on the testbench's own top dut (.*) is not this error (no reader builds the testbench, the simulation runs). A named connection with the same widths (102c_named_conn_width) is an assignment, padded or cut by all three tools, and builds; the same .* with the signals declared the port's width (102b_dotstar_param_equal) builds in yosys' own reader. A port whose type is not a plain vector (input int a to a logic [31:0] a) is still [slang-refused], with slang's own words.
Numbers. From the closing run of this tree: Vivado-supported probes passing all four stages 88/113 → 95/118, all four stages 97/137 → 104/143, synthesis 111/137 → 119/143, known gaps 26 → 24. Against #32's closing table the 137 old rows are byte-identical but three: 22b_interface_own_file and 23c_package_own_file go from fail fail - fail to pass pass pass pass (the two gaps closed), 56b_unit_typedef_file from fail fail - fail to pass pass fail pass (still a gap, its netlist half below); the six new rows are the six probes under Tests. 102_dotstar_param_width is not a gap: its row expects the refusal. test/run.sh: passed 325, failed 0, known gaps 24 in 30 min 41 s (the board check was skipped, SKIP flash on the board: no board: no FTDI device on the USB bus). The listed count goes 351 → 357 (the six probes). test/fuzz/run.sh 1 500, the same 500 seeds, before this update (#32's closing run of the released tree):
fuzz: refused-coded by code: unique-case-overlap 24; slang-refused 20; package-file-not-given 14
fuzz: ok 416 of 500; silent-wrong 19; warned 7; refused-coded 58; refused-uncoded 0; crash 0; sim-refused 0
After, in this close (JOBS=3, 19 min 27 s):
fuzz: refused-coded by code: unique-case-overlap 24; implicit-port-width 22
fuzz: ok 428 of 500; silent-wrong 19; warned 7; refused-coded 46; refused-uncoded 0; crash 0; sim-refused 0
Seed by seed: 466 seeds keep their result; 12 go package-file-not-given → ok (9, 72, 78, 168, 185, 186, 279, 307, 316, 329, 374, 472: a package in its own file, now read); 2 go package-file-not-given → implicit-port-width (192, 200: once the package file was read, the .* width mismatch behind it showed); 20 go slang-refused → implicit-port-width (16, 35, 79, 87, 106, 129, 163, 179, 189, 196, 304, 315, 323, 349, 355, 365, 433, 434, 444, 454: the same refusal, now the product's line with the fix). No ok went anywhere, no seed went to silent-wrong, the 19 silent wrongs and the 7 warned are the same seeds as before. The update's claim text said ok 430 and implicit-port-width 20; the measured numbers are 428 and 22, the integrator's two full runs before its last commit said the same 428 and 22.
Tests. Six probes with their expect.tsv rows, each quoted message checked against the run's output by the probe runner: 102_dotstar_param_width (rtl fail, synth fail, bit fail: the [implicit-port-width] line from sim and from bit, no top.bit), 102b_dotstar_param_equal, 102c_named_conn_width (both build, 8 LUT and a named connection the readers resize), 103_package_file_two_users (a package file imported by two module files, no include anywhere: EQV PASS: 1048576 output bits compared, all equal), 103b_two_packages_order (a_pkgimportsz_pkg: the product reads z_pkg.sv a_pkg.sv design.sv), 103c_package_file_included(23c's old fix,include on line 1: the file is left to the include). Three rows rewritten: 22b, 23c(both now pass all four stages) and56b(rtl, synth and bit pass; its netlist half is the harness's, see Open). The integrator ran the 97 probes it listed as the blast radius of the file-order change throughtest/sv/run.shafter the change:matches expect.tsv: 97/97, 0 changed rows. The judge's own angles passed: a parent's own narrower port via .*, a wide signal cut, an interface file that imports a package file, a file holding both a package and a module. The judge's one medium finding in the repro (a package file included only by the testbench was dropped from the design list, and the fix led to iverilog's "I give up") is closed by [included-twice], measured in this close on 23c with the include moved into tb.sv. No new check went into test/run.sh` beyond the probes. Mail suites: 9 suites, each exit 0 (activation SQL 142, SQL 187, mail flow 60, sender idempotency, lifecycle 112, usage, integration, pages 139, Chromium and WebKit 158; 0 failures). Site check: 116 pages, 2128 links, 0 failures.
Open.
56b_unit_typedef_filestays a known gap in the table: the product readstypes.svfirst and builds (pnr ok: 2 LUT, 2 FF), but the netlist comparison intest/sv/eqv.shputs only package and interface files first and readsdesign.svbeforetypes.sv, so its RTL half stops at a syntax error (EQV FAIL: yosys could not list the RTL's registers). The same harness reads package files in alphabetical order (103b:EQV SKIP, the testbench half alone checked the netlist) and hands aincluded package file twice (103c:EQV SKIP`). The harness, not the product, was out of this update's files.dewfpga bitprints yosys' raw line,ERROR: Width mismatch between wire (4 bits) and port (8 bits) for implicit port connectionsub_out' of cell top.u ($paramod\sub\N=...), before the coded[implicit-port-width]line; in the.namecase yosys prints twoResizing cell port` lines, one per port, and the second still passes through after the coded line.- A typedef
.svhthat no fileincludes: the first[undeclared-name]line names the file and theinclude fix; the two lines after it, for the enum's members, still say "not declared anywhere ... check the spelling" (the judge's repro). - Vivado's own behaviour on a
.*or.namewidth mismatch is unverified: UG901 v2023.2 Ch.10 p.281-282 lists both forms as supported and does not say what Vivado does when the widths differ, and no Vivado ran; the102row's vivado column isunverified, the message does not claim what Vivado does. In which order Vivado compiles a file that holds only declarations is not in the guide either. - The
Resizing cell portrule refuses only an instance line that connects the port as.portwithout(: a named or positional connection with a width difference passes through with yosys' and iverilog's warnings, as the standard says it should (an assignment), but without a line of the product's. - Two module-less files that name each other (a cycle) fall back to the order given, without a word.
- The two unexplained silent-wrong families of #31 (netlist
xbefore the first edge; known bits differing after the start) are untouched; #34 owns the first.
#34 · The netlist the build writes starts every register as the bitstream does: a register without a start value at 0, a set or preset register at 1 · 7 October
The first of the two unexplained silent-wrong families #31's generator left: the netlist prints x before the first clock edge where the simulation knows a 0 or a 1. A lab design declares its state register without a start value and reads it before the reset button is ever pressed:
typedef enum logic [1:0] {IDLE, RUN, DONE} state_t;
state_t state, next;
always_ff @(posedge clk) if (btnC) state <= IDLE; else state <= next;
always_comb if (state == DONE) led = 16'hFFFF; else led = '0;
dewfpga bit prints the same lines before and after this update (pnr ok: 3 LUT, 3 FF, clk 830.56 MHz (PASS at 100.00 MHz), top.bit 2.2 MB); what changes is a file it writes into the lab folder, top.json, the netlist nextpnr-xilinx places and routes into top.bit, and the one the probe runner and the generator simulate against the student's own testbench as their third stage. Before, yosys left every flip-flop whose register had no start value at INIT = 1'hx (in the netlist written as Verilog: INIT(1'hx) on each of the three flip-flops of this state register), and the netlist simulation started them at x, so the case and the if on state drove every LED to x. The probe's testbench on that netlist:
FAIL: before the first edge led=xxxxxxxxxxxxxxxx, expected all off
The board never shows an x: nextpnr-xilinx programs a flip-flop without a start value at 0 when it is an FDRE, FDCE, LDCE or LDPE and at 1 when it is an FDSE or FDPE (a register with a synchronous set or an asynchronous preset), and the same for their falling-edge _1 forms; that is its def_init in xilinx/fasm.cc, and it was checked on the product's own FASM (a design with 4 FDRE, 1 FDSE and 1 FDPE: the ZINI bit on exactly the 4 FDRE). So the generator's third stage reported a difference that was the netlist simulation's, not the board's, and could not see past it to what the board does. Now the build runs yosys' setundef on the INIT parameters before write_json: -zero on FDRE, FDCE, LDCE, LDPE and their _1 forms and on every LUT, RAM and SRL cell, -one on FDSE, FDPE and their _1 forms. The same netlist now carries INIT(1'h0) on the three flip-flops, and the same testbench prints
PASS
Only x bits are replaced: a start value the student declared (logic [3:0] cnt = 5;) stays as written on an FDRE and on an FDSE alike (the judge checked a declared 0 on an FDSE survives -one), and every LUT, RAM and SRL INIT is already known and stays. The student's own fix for a design that must not depend on this is still a start value in the declaration (UG901 v2022.2 Ch.4 p.79: "To initialize the content of a Register at circuit power-up, specify a default value for the signal during declaration"; p.244: the value is "carried as an INIT attribute"). What Vivado programs for a register with no start value is not in the guide; the values above are nextpnr-xilinx's, measured, not Vivado's.
For the generator, seed 53 is the picture. Before:
s53 silent-wrong MISMATCH: cycle 0 rtl: 00000000xxxxxxxx xxxxxxx 0011 0 net: 00000000xxxxxxxx 00xxxx1 00x1 0
After:
s53 ok
Numbers. From the closing run of this tree: Vivado-supported probes passing all four stages 95/118 → 97/120, all four stages 104/143 → 106/145, synthesis 119/143 → 121/145, known gaps 24 → 24. Against #33's closing table the 143 old rows are byte-identical; the two new rows are the two probes under Tests. test/run.sh: passed 327, failed 0, known gaps 24 in 26 min 6 s, the second of two runs (the first ended passed 326, failed 1: the three pages still quoted 357 checks and 143 probes where --list prints 359 and 145, the two new probes uncounted, fixed in this close; the probe table of both runs is identical). The board check was skipped, SKIP flash on the board: no board: no FTDI device on the USB bus. The listed count goes 357 → 359 (the two probes). test/fuzz/run.sh 1 500, the same 500 seeds, before this update (#33's closing run of the released tree):
fuzz: refused-coded by code: unique-case-overlap 24; implicit-port-width 22
fuzz: ok 428 of 500; silent-wrong 19; warned 7; refused-coded 46; refused-uncoded 0; crash 0; sim-refused 0
After, the integrator's run at the final tree (JOBS=3, finished after its report; this close did not run the 500 again, the product files of the tree are the same, this close changed documentation and counts only):
fuzz: refused-coded by code: unique-case-overlap 24; implicit-port-width 22
fuzz: ok 436 of 500; silent-wrong 11; warned 7; refused-coded 46; refused-uncoded 0; crash 0; sim-refused 0
Seed by seed: 8 seeds go silent-wrong → ok (53, 151, 157, 195, 281, 393, 479, 487), the 11 that stay silent-wrong (21, 62, 65, 121, 134, 161, 162, 214, 263, 302, 494) were silent-wrong before, no seed moved the other way, the 7 warned and the 46 refused are the same seeds. The judge's own partial run of the same command (205 seeds, stopped at its budget) agreed seed for seed up to 205. The update's claim text said 436 and 11; the measured line says the same.
Tests. Two probes with their expect.tsv rows: 104_fsm_before_first_reset (the 3-state enum FSM above, case and if on a register with no start value, led checked before any clock edge, then through a reset, IDLE → RUN → DONE → IDLE) and 104b_sync_set_start_one (a 1-bit armed register set by the reset button and cleared by a switch, an FDSE, beside a 16-bit counter on FDRE; led must be all on before the first edge: the FDSE starts at 1, the FDRE at 0). Both were pass pass fail pass on the released tree (a silent wrong: the simulation, the synthesis and the bitstream pass, the netlist stage fails) and are pass pass pass pass now, with EQV PASS: 262144 output bits compared, all equal on 104. The integrator's guard after the change: the blink template's FASM equals test/golden/blink.fasm (the bitstream itself does not change: nextpnr already read an x INIT as its default, the netlist now says so too), and the eight probes with a register, RAM or SRL of every kind (37_var_init, 31_reset_styles, 31b_negedge_set, 29_shift_reg_srl, 28_dist_ram, 27_readmemh_rom, 10_enum_fsm, 101c_always_latch_race) matches expect.tsv: 8/8; then the 104 probes that passed all four stages, matches expect.tsv: 104/104, 0 changed rows. The whole table ran once more in this close, see Numbers. A probe draft written as next = sw[0] ? RUN : IDLE hit iverilog's "This assignment requires an explicit cast" (#31's note), so 104 writes the transitions as if/else inside the case. No new check went into test/run.sh beyond the probes. Mail suites: 9 suites, each exit 0 (activation SQL 142, SQL 187, mail flow 60, sender idempotency, lifecycle 112, usage, integration, pages 139, Chromium and WebKit 158; 0 failures). Site check: 116 pages, 2130 links, 0 failures.
Open.
- The claim holds before the first clock edge. yosys recodes the 2-bit enum of probe 104 as three one-hot flip-flops, and their all-zero start is none of the three states: with no reset pressed and
sw[0]on, the netlist (and so the board) keepsled=0000on every clock until the button is pressed, while the student's simulation, whosexstate takes thedefaultbranch to IDLE, runs IDLE → RUN → DONE and showsffffafter the first edge (the judge's repro, measured in this close). The product says nothing about a register read before its first reset; the netlist now shows what the board does, which is what makes this measurable. - The 11 seeds that stay silent-wrong are now of one kind: a known bit that differs at the first cycle (s134:
rtl: 0000000000000000 net: 0000000000001111, the RTL'sxtook acasedefault or anelse, the board runs from 0), the same difference as the point above, not anxany more. Seed 302 also differs onsegat 193 of 300 later cycles, a combinational difference no start value explains; seeds 121 and 214 (known bits differing after the start) stay #31's second, unexplained family. None of the 11 was touched. top.jsonstill carriesxparameters: two$specrulecells withT_LIMIT_MIN,T_LIMIT_TYPandT_LIMIT_MAXof 32xbits each in every design (timing checks, no INIT, not read by the netlist simulation).- What Vivado programs for a register declared without a start value is not in UG901 (v2022.2 p.79 and p.244 describe a declared value only), so the start values the new rows state are nextpnr-xilinx's, checked on the product's FASM; the rows'
vivadocolumn says the construct is supported, not what Vivado would show. - 104's
expect.tsvtext and the probe's comment first said the state register starts at "INIT = 0 (IDLE)"; in the netlist 0 is the one-hot no-state (the judge's finding). Both reworded in this close; the probe runs as before,matches expect.tsv: 1/1. - The board was not plugged in during this close (no FTDI device on the USB bus): no flash, no board claim for the two probes; the netlist's start values were checked against the FASM, not against a lit LED.
#35 · A register read before its first reset: dewfpga sim starts it where the board starts it, and the netlist keeps the state codes the student wrote · 8 October
The open item #34 left, closed from both sides. The lab design is the same state machine, declared without a start value and read before the reset button is ever pressed:
typedef enum logic [1:0] {IDLE, RUN, DONE} state_t;
state_t state, next;
always_ff @(posedge clk) if (btnC) state <= IDLE; else state <= next;
always_comb begin next = state; case (state) IDLE: next = RUN; RUN: next = DONE; DONE: if (sw[0]) next = IDLE; default: next = IDLE; endcase end
assign led = (state == RUN) ? 16'hFFFF : '0;
Before, dewfpga sim started state at x, as iverilog starts every variable, and a testbench that looks before the first clock edge saw it (measured on the released tree in this close):
iverilog -g2012 -o top_sim design.sv tb.sv
FAIL: before the first edge led=xxxxxxxxxxxxxxxx, expected all off (IDLE, the reset value the board starts in)
and dewfpga bit printed pnr ok: 3 LUT, 3 FF, clk 645.58 MHz (PASS at 100.00 MHz): three flip-flops for a two-bit register, because yosys' fsm pass re-encoded it one-hot, and the three started at 0 (#34), which is none of the three states. The netlist under the same testbench, with no reset pressed: FAIL: after the first edge, no reset pressed, led=0000000000000000, expected all on (RUN: the board starts in IDLE and moves on). Two different wrongs: the simulation did not know where the board starts, and the board did not start in a state at all. Now dewfpga sim prints
iverilog -g2012 -o top_sim design.sv tb.sv
note [sim-starts-as-board]: 1 register without a start value starts as the board does, not at x: state at 0 (its reset value). Vivado's simulator shows x there until the first reset; a start value in the declaration (state_t state = IDLE;) is honoured by every tool (UG901 v2022.2 Ch.4 p.79). https://nosey-dewdrop.github.io/dewfpga/errors/sim-starts-as-board/
PASS
and dewfpga bit prints pnr ok: 3 LUT, 2 FF, clk 624.22 MHz (PASS at 100.00 MHz): the register keeps the student's two-bit codes, 00 is IDLE, and the netlist under the same testbench prints PASS, moving to RUN at the first edge as the simulation does (EQV PASS: 262144 output bits compared, all equal).
How the simulation knows: before iverilog runs, the design's registers and memories are listed with yosys (read, proc, flatten, opt_clean, memory_collect, opt_dff; yosys-slang when yosys' own reader refuses the code), and a new product file, templates/sim_starts.py, writes one more module that is compiled with the testbench and at time 0, after the design's own declaration values and the testbench's time-0 statements ran, sets every register bit that is still x to the board's value: its reset value when it has a reset (a sync reset to 5 starts at 4'b0101, an async reset to 4'b1010 at 4'b1010: the flip-flops yosys maps to FDSE or FDPE power up at 1, the FDRE and FDCE at 0, #34's values), 0 without a reset, 0 for every word of a LUT RAM nobody wrote. A declared start value is never x and stays. The module is removed once iverilog has compiled it (a file left behind would reach dewfpga bit). Nothing to start, as in blink, whose two registers have declared values: no note, no file; the design iverilog refuses (the sim-from-build path) gets the same module from the build's own view of the design. Measured on probe 105_reset_value_start (four registers: sync reset to 5, async reset to 4'b1010, no reset, declared 9): before, FAIL: before the first edge a=xxxx b=xxxx c=xxxx d=1001, expected 0101 1010 0000 1001; after, note [sim-starts-as-board]: 3 registers without a start value start as the board does, not at x: a at 4'b0101 and b at 4'b1010 (their reset values), c at 0 (no reset). and PASS, with pnr ok: 27 LUT, 16 FF and EQV PASS: 262144 output bits compared, all equal on the netlist. On 105c_memory_before_write (a 4-word LUT RAM read before any write): before FAIL: before any write mem[0] reads xxxxxxxx, expected 0 (the board's LUT RAM starts at 0), after note [sim-starts-as-board]: 1 memory without a start value starts as the board does, not at x: mem at 0 (a LUT RAM). and PASS. A table that $readmemh fills only in part is not started: yosys folds its unloaded words as don't-care and the netlist reads some value there (measured by the integrator: CC where the simulation shows x), so those words stay x and the note says so, with the fix (load every word, or fill the table first).
How the netlist keeps the codes: before synth_xilinx reads the design, every named wire gets fsm_encoding = "none", which yosys' fsm_detect honours, so no register is re-encoded; the attribute is unset again after synthesis (as a net attribute it moved nextpnr's placement: blink's FASM differed in 5 attribute lines and 254.78 MHz for 278.71, measured by the integrator), and blink's FASM is byte for byte test/golden/blink.fasm (0 differing lines, measured in this close after the fix below). A student's own attribute is replaced too, with a note at its line, because left in place it kept the recoding. Measured on a ctl module with (* fsm_encoding = "one_hot" *) logic [1:0] state;: before, pnr ok: 3 LUT, 3 FF, 3 FDRE all INIT(1'h0), the netlist's testbench FAIL: after first edge no reset led=0000000000000000 expected all on (RUN); after,
design.sv:3: note [fsm-encoding-kept]: (* fsm_encoding = "one_hot" *) on state is not applied: the netlist keeps the 2-bit codes the declaration gives state, so its power-up (every flip-flop at 0, as the board starts a register without a start value) is the state with code 0, the one dewfpga sim starts in; re-encoded, the flip-flops would start in no state until the first reset (the netlist and the board would sit dark where the simulation moved on). Vivado applies the attribute (UG901 FSM_ENCODING), and the guide does not say what its re-encoded register holds before the first reset. Fix: none needed for this board, and the attribute may stay for Vivado; to start state in a state other than the one with code 0, give it a start value in its declaration (state_t state = RUN;), which every tool honours. https://nosey-dewdrop.github.io/dewfpga/errors/fsm-encoding-kept/
pnr ok: 3 LUT, 2 FF, clk 624.22 MHz (PASS at 100.00 MHz) (full log: top.log)
2 FDRE, and the netlist's testbench PASS. The note is for a register only: state_t state, next; puts the attribute on both, and next, written in always_comb, gets the none silently (the integrator's fix round 2, after the note fired twice). Until this close that link led nowhere: the note was the one code the Makefile prints with no page in the catalog, so the page was written from the measurement above and a check now reads every code the Makefile and the CLI print against docs/errors.md.
What else the build does before synth_xilinx now, from the integrator's fix round 3 after the judge's repro: a register with no reset and no start value that never changes (its D is its own Q, fuzz seed 29) was folded by yosys to its init, x, and every LED behind it was an OBUF of 1'hx with pnr ok: 0 LUT, 0 FF and no word about it, where dewfpga sim now showed 0011001100110011; such a register gets init 0 first, where the simulation and a surviving FDRE start it. A memory with no start value gets INIT 0 as yosys' memory cell, where the simulation starts its words; without it yosys folded a memory every write of which stored the same constant (seed 33) into that constant, and the netlist showed it at cycle 0 where the simulation showed 0.
This close found two regressions of that round in the full run and fixed both: the new passes ran before yosys' check, whose warnings two refusals read. 39_undeclared_name (a typo, summ for sum) built a bitstream, pnr ok: 0 LUT, 0 FF, with Warning: Wire top.\led [0] is used but has no driver.: opt_clean had removed the undriven implicit wire, so the warning named led [0] instead of summ, and the awk found no implicit wire of that name. 05_two_block_driver (a register written from two always blocks) stopped on a raw ERROR: Conflicting init values for signal 1'0 (\led [1] = 1'x != 1'0). instead of the coded two-always-drivers line: opt_dff had folded the two conflicting flip-flops before any check saw them. Now check runs right after proc, where synth_xilinx's own first check ran before (its prepare stage is proc; check; ...; opt_clean; check), and both probes refuse as the released tree did: design.sv:4: ERROR [undeclared-name]: summ is not declared anywhere and nothing drives it ..., exit 2, no bitstream, and design.sv:3 and design.sv:4: ERROR [two-always-drivers]: cnt is written from two always blocks ... with yosys' multiple conflicting drivers for top.\cnt [3] lines around it and the raw init line dropped once the coded one is out.
Numbers. test/run.sh on this tree: the first full run passed 328, failed 2, known gaps 24 (the two regressions above, 29 min; probe table synthesis 125/148, all four stages 109/148, Vivado-supported probes passing all four stages 99/122, known gaps 24, the 125 counting the typo that built); after the fix passed 331, failed 0, known gaps 24 in 23 min 37 s, with the probe table synthesis 124/148, all four stages 109/148, Vivado-supported probes passing all four stages 99/122, known gaps 24; between the two runs only the 05 and 39 rows changed, from bad to ok. Against #34's closing table: Vivado-supported probes passing all four stages 97/120 → 99/122, all four stages 106/145 → 109/148, synthesis 121/145 → 124/148, known gaps 24 → 24; the three new rows are the three probes under Tests (105c is unverified, so it adds to the stages and not to the Vivado-supported count), and the 145 old rows are byte for byte #34's closing table. The listed count goes 359 → 363 (three probes, one check). The board check was skipped, SKIP flash on the board: no board. test/fuzz/run.sh 1 500, the same 500 seeds, JOBS=3. Before, the line #34's note published for the released tree:
fuzz: ok 436 of 500; silent-wrong 11; warned 7; refused-coded 46; refused-uncoded 0; crash 0; sim-refused 0
After, this close's own run of the final tree (25 min):
fuzz: refused-coded by code: unique-case-overlap 24; implicit-port-width 22
fuzz: ok 446 of 500; silent-wrong 2; warned 6; refused-coded 46; refused-uncoded 0; crash 0; sim-refused 0
Seed by seed against the released tree's 500 (the integrator's run of the base commit, the same results the #34 note published): 9 seeds go silent-wrong → ok (21, 62, 65, 121, 134, 161, 162, 263, 494), one goes warned → ok (327: the one-LED difference at cycle 29 that its latch warning forgave is gone; not analysed further), the 2 that stay silent-wrong (214, 302) were silent-wrong before, no seed moved the other way, the 46 refused are the same seeds with the same codes. The integrator's run of its tree, before this close's check fix, gave the same line.
Tests. Three probes with their expect.tsv rows: 105_reset_value_start (the four registers above), 105b_fsm_first_edge (the state machine above, read at the first edge before any reset) and 105c_memory_before_write (the LUT RAM above). On the released tree, measured in this close with the base CLI: 105 FAIL ... a=xxxx b=xxxx c=xxxx d=1001 in the simulation while its netlist passed (a silent wrong), 105b failed in the simulation (x) and on the netlist (dark after the first edge, 3 FDRE), 105c failed in the simulation while its netlist passed. On this tree all three are pass pass pass pass. 104_fsm_before_first_reset's row is reworded: the register is 2 FDRE now, its all-zero start is IDLE, and the netlist leaves it at the first edge as the RTL does. The six probes this close touched or doubted (05, 39, 104, 105, 105b, 105c) through the probe runner after the fix: matches expect.tsv: 6/6; the whole table ran in the full run, see Numbers. New check in test/run.sh: every code the Makefile and the CLI print has an entry in docs/errors.md (57 codes read from the Makefile's ERROR [..], warning [..], note [..] strings and its fmt("note", "..") calls and from the CLI); its mutation, the catalog without the new entry, prints no entry in docs/errors.md: fsm-encoding-kept and fails. The integrator's own guards before this close: the 10 student designs of its breaker (a submodule's register, a non-zero reset value, two machines, a generate loop, explicit codes, a packed struct, the one-hot attribute, a localparam machine, a testbench whose clock starts high) all PASS in the simulation and on the netlist with the note, and the 19 probes with a register, RAM, SRL or enum of every kind matches expect.tsv: 19/19. Mail suites: 9 suites, each exit 0 (activation SQL 142, SQL 187, mail flow 60, sender idempotency, lifecycle 112, usage, integration, pages 139, Chromium and WebKit 158; 0 failures). Site check: 118 pages, 2165 links, 0 failures.
Open.
- The judge's headline against the integrator's first version stands as history, not as the result: that version's own 500-seed run went
silent-wrong 11 → 14, because starting the simulation where the board starts exposed netlists that anxin the RTL had been hiding (seed 29'sOBUFof1'hx, seed 33's folded memory); fix round 3 above answers those, and the line this close measured is under Numbers. - Seeds 214 and 302 stay silent-wrong, #31's second family: a known bit that differs after the start (214 at cycle 0 in one LED, 101 of 300 cycles,
209 LUT 36 FF; 302 from cycle 2 onseg), no start value explains them. Untouched. - A netlist whose output is driven by a constant
xstill printspnr okwith no word about it (the judge's repro on seed 29 before fix round 3; that seed is fixed, the general case is not detected). - A
$readmemhtable loaded in part: the simulation keepsxin the words nobody loaded and the note names the gap; the netlist reads some value there (CCon the integrator's design), anddewfpga bitsays nothing about it. The fix is in the note's text only. - The note is printed on every
dewfpga simof a design with a register or memory that has no start value, not once; a lab design with ten such registers gets a long line each run. - What Vivado's simulator shows for these registers is
xuntil the first reset (as iverilog did); what Vivado programs for a register without a start value is not in UG901, nor what its re-encoded state register holds before the first reset, nor what a distributed RAM holds before its first write (105c's row isunverifiedfor that reason). The values here are the open chain's, measured on its own netlists and FASM. - The judge noted
no clocked paths, timing not applicableprinted beside a non-zeroFFcount on one of its designs; not reproduced in this close (everypnr okline measured here with flip-flops carried a clock frequency). - The integrator's protect run of the 96 untouched probes was stopped after 3; the full run of all 148 in this close is what stands for it.
- The board was not plugged in during this close: no flash, no board claim; the netlist's start values were checked against the FASM and the netlist simulation, not against a lit LED.
Unreleased: preserve vector indices in pin validation
A parameterized port of width one ([0:0]) was mistaken for a scalar, and ports such as [4:1] were renumbered from zero. Both were rejected before place-and-route even with correct XDC assignments. The checker now uses Yosys vector metadata and declared offsets, including ascending ranges. The student's HDL and pin assignments do not need to change.
Validation: 35 focused checks over seven real Yosys port shapes, including missing/commented pins, missing IOSTANDARD and duplicate pin assignments. The old checker fails this regression. Probes 96 and 96b pass pin validation and place-and-route; RTL simulation and netlist equivalence are checked, and their FASM is converted to bitstreams. The restricted execution environment blocks CLI simulation's /dev/fd redirection and the frame converter's default /dev/stdout; validation uses the same Makefile simulation target and an explicit frame output filename. Full CLI success, VS Code GUI and physical board behavior are not claimed for this candidate. No full suite or deployment was run.
Unreleased: editor tasks find the installed Homebrew tools
VS Code tasks and the top-module picker now append /opt/homebrew/bin to the inherited executable search path. A GUI environment without shell setup could find the dewfpga executable but report the installed synthesis, simulation and programming tools as missing. Existing tool overrides keep precedence. Both generated user tasks and new-project tasks receive the fallback.
Validation: task writer 71 checks and extension 36 checks passed. The actual CLI check succeeds with the generated task environment starting from system-only PATH; an absent toolchain is still refused. The real CLI top picker also resolves the nested mixed-language project from that environment. These are process and API tests, not a claim of physical board or GUI acceptance. Existing installations need the updated companion/tasks via dewfpga vscode; existing project-local tasks are not silently rewritten. Not deployed.
Unreleased: build without device-file output redirection
CLI simulation keeps stdout live and filters stderr from a temporary file after make returns, preserving its exit status. Editor diagnostics use a merged pipe for absolute paths. Frame conversion writes its normal output filename directly. This removes the /dev/fd and /dev/stdout dependency that prevented validation in restricted execution environments; no permissions were changed. Simulation stderr is now delivered when make completes, rather than streamed during execution.
The installed editor task's command and environment were invoked on the nested multi-file Working Lab fixture. Simulation passed; build produced a 2.2 MB bitstream and passed timing. Programming reached openFPGALoader and returned board-not-found. This validates the command path, not a GUI keystroke or physical board behavior. Failing-testbench and simulation-timeout regressions both passed.
Follow-up: probes 96 and 96b now pass all four stages through the unmodified probe runner and the actual CLI, without manually invoking intermediate stages. This closes their earlier restricted-environment validation gap, not physical board acceptance.
Unreleased: distinguish programmer access from macOS USB visibility
A failed FTDI open no longer asserts that macOS cannot see the board. The message and guide distinguish the programmer's access from physical presence. The existing board-not-found code and nonzero exit behavior are preserved. Four controlled programmer outcomes pass without synthesis or physical USB access; a failure does not erase or modify the existing bitstream.
Local candidate · Your own project, from editor to board · 8 October
The short CLI guide now starts with the user's own design and pin file, followed by dewfpga flash, which builds and loads the board. VS Code's task setup and run action are explicit. Local testbench simulation and browser simulation have their own paths; neither is presented as a prerequisite for programming the board. The English and Turkish README introductions follow the same route. Desktop and mobile layout checks passed, and the source-site check found no broken links across 118 pages. These are local changes, not a published installation test.
The browser simulator previously discarded the source numbering of ports. Correct pin files for ascending, nonzero and negative ranges could be rejected or connected to the wrong internal bit. The candidate keeps source indices through pin binding, board labels and code-to-board hover. Its focused regression uses a new circuit with renamed ports, a separately computed truth table, actual switch clicks and LED rendering. Both Chromium and WebKit passed the focused checks; changing XOR to AND changes the displayed result, and missing or conflicting pins stop the run. The existing Chromium board checks also passed. The native reader's single_bit_vector net attribute is now preserved too: a known [0:0] port requires [0], and the bare-name negative control passes. The slang reader omits that attribute; unmarked width-one ports retain the browser's existing bare-name / [0] compatibility, so this is not a claim of universal pin-alias parity. Full-suite closure is recorded below; physical LED observation remains pending. A separate own-project SRAM upload completed successfully.
Separately, three small projects outside the lab corpus express the same function with a for loop, a statically bounded while loop and a reduction expression. Each passed CLI simulation, bitstream generation and the generated netlist's independent 16-row truth table; their source files were unchanged. This proves these examples, not all SystemVerilog. Two previously recorded random designs reproduced behavior mismatches even though the measurement runner exited successfully. Focused fixes now pass: repeated products retain both terms, and an unwritten register is initialized before optimization can erase it. Six arithmetic circuits and four startup circuits match independently computed expectations in RTL simulation and synthesized netlists. The original failing designs also match after the fixes. The earlier #35 assessment that both remaining cases were unrelated to startup was incorrect for seed 214. DSP multipliers remain; surrounding adders and registers use more LUT/FF resources. These checks do not constitute physical arithmetic measurements or a new full random-design campaign.
The CLI now compiles compatibility-normalized copies instead of rewriting the user's saved source. Inherited port directions and unnamed instances continue to build; successful and failing builds preserve source bytes, diagnostics retain the original file and line, and editing a source or included header changes the next result. The direct Makefile path is covered by an actual forced compile as well.
An updated build recipe or checker now invalidates cached simulation and synthesis outputs. Previously, the fixed chain could call a known-wrong old bitstream “up to date.” The regression preserves file timestamps while changing tool content, then checks both the necessary rebuild and subsequent cache reuse.
A module header may import packages before its port list. Those import semicolons previously made the scanner classify a real design as a testbench and select its child as top. The CLI now finds the intended design and preserves original line locations through normalization. The browser's own-project acceptance uses this form rather than changing the user's code into an easier example.
The complete Chromium suite passed in 359.7 seconds with unchanged source hashes, including the new own-project journey and one-bit-vector negative check. Import exclusion notices now remain visible after the automatic compile. The full WebKit attempt reached a new test assumption: WebKit does not deliver hidden .Xil files to the application. The test now checks the actual FileList; the corrected journey and the remaining hardware-validation stage passed separately. Earlier passing stages were not unnecessarily repeated.
The complete CLI run recorded 332 passes, eight failures and 21 known gaps. The original failure record is retained. Targeted follow-ups closed all eight failures: old PATH/normalizer test doubles, a missing test SDK, an equivalence reader that needed the same lexical source preparation and language mode as the product, and outdated probe expectations. The equivalence check still compares RTL with a separately synthesized netlist; all 54 controls passed, including 27 deliberately wrong netlists that were still rejected. The full 148-row record plus six targeted replacement rows has 129 ordinary outcomes and 19 known gaps, with 114 passing all four stages. This is a reconciled record, not a second full run. Probe 98 still has only its netlist testbench check because its independent equivalence bench cannot compile. Missing MMCM/BRAM models and a warned latch race remain explicit limits.
The final package was installed in an isolated directory and built the actual browser-downloaded project. Real Basys3 SRAM programming passed, including restoring the own-project parity circuit after the suite's blink checks. Physical LED observation and an actual VS Code GUI run remain unverified; an isolated extension and task installation is not substituted for that GUI observation. Nothing here claims a published deployment or universal SystemVerilog support.
Local candidate · An empty folder in VS Code, and a misspelled endmodule · 14 October
An empty folder now writes its own design file. A student opened an empty lab1 folder in VS Code. Nothing in the window pointed at dewfpga, and the coding agent they asked did not know it, so the student wrote a 10-line .xdc and a 22-line Makefile by hand next to the design. The same design, alone in a folder, builds to a bitstream in 4.6 seconds with no .xdc and no Makefile. Now an empty folder asks one question, Create top.sv. The button writes top.sv (16 switches to 16 LEDs, with the port names that need no pin file listed at the top), opens it under the Run button, and writes the agent notes beside it. A folder with files, a window with two folders, or a "Never" answer gets no question; an existing top.sv is never overwritten. The same action is the command dewfpga: new Basys3 design in this folder (top.sv).
The agent notes start with the one-file path. AGENTS.md used to describe only the folder commands. Its first section now says one .sv is enough, names the automatic ports, and tells the agent never to ask for a Makefile, an .xdc or a build script, and never to install other FPGA tools. The file stays within its 60-line limit.
A misspelled endmodule is named with its line. lab1_top.sv ended in endmodulek. The source scan pairs each module with the next endmodule, so lab1_top dropped out, the two modules it instantiates looked unused, and the CLI said two-tops and suggested full_adder as the top. Now:
lab1_top.sv:27: ERROR [module-without-endmodule]: module lab1_top (lab1_top.sv:21) has no endmodule: line 27 reads endmodulek. Fix: write endmodule on line 27.
With no word like endmodule in the module, the message names the line where the next module starts. Single-file Run now prints the scan's message as it is, and its two-tops fix names dewfpga run <file> --top NAME instead of the folder command.
Tests: three new single-file checks (misspelled, missing, a real two-top file) fail on the installed 1.2.0 and pass now. The scan raised no alarm on any of the 364 .sv/.v files in the repository. The VS Code extension suite passed 41/41; the new checks build the written top.sv with the real chain (32 ports, all pinned automatically). The full suite ran once: 340 passed, 3 failed, 19 known gaps unchanged. The three were closed and rerun: AGENTS.md had grown to 74 lines (limit 60), site/llms.txt lacked the new code, and the Python bytecode the tests left in templates/__pycache__ held this machine's home path (the suite now writes no bytecode). The SystemVerilog probes are as on 8 October: 114 of 148 pass all four stages. Not tried: the button in a real VS Code window and programming the board with this change.
Local candidate · Run shows its error, reads the lab folder, and prints only what a student acts on · 15 October
A failed Run says why. The play button used to start dewfpga run as the terminal's own process, so a failure reached the student as VS Code's terminal process terminated with exit code: 1 and nothing else. That is what happened on a file that held a pasted terminal log instead of a design. Now the extension starts the command itself and keeps its output in a terminal that stays open. The first error is a notification (lab1_top.sv:27: module lab1_top ... has no endmodule: line 27 reads endmodulek), every file:line message is underlined in its file and listed in Problems, and a success says the file is on the Basys3.
Run reads the lab folder. A lab's top usually instantiates modules from the files beside it. Run used to read only the file it was started on and stopped at unknown module 'full_adder'. Now it reads the modules the file uses, transitively, from the other .sv/.v files of its folder, plus the package and interface files they name, and prints Also reads: full_adder.sv. Run on a submodule's file (full_adder.sv) builds the one top that uses it (top: top, in top.sv). A folder file the design does not use (an old try that does not compile, a testbench) is never read.
The output lost three lines nobody could act on. A design with no clock printed a no-create-clock note about pins.xdc, a file the student never wrote, and nextpnr's Warning: No clocks found in design. The note now prints only when the design has a clocked cell, and the warning is dropped like the [current_design] lines. Run's full log: path named a file in a deleted temporary folder; it now names the kept copy, also after a failed build. A scan message named the staged copy (no design module in source.sv.); it now names the student's file. Lab 1's Run output went from 8 lines to 6.
Tests: the VS Code suite passed 42/42; its new check runs the real CLI on a misspelled endmodule and reads the notification, the underlined line (5) and the terminal text. Two new folder checks (a package, a submodule file, the top file, a testbench and a broken old try in one folder) build from either file, and the netlist gives the full adder's 8 rows right; a used sibling's error is named with its own file and line. Both fail on the installed 1.2.0. The pin checks (23) and the golden bitstream are unchanged. The full suite ran once on this candidate: 342 passed, 2 failed. One was this change: probe 97's block RAM is a clocked cell, and there nextpnr's No clocks found is the gap's own evidence, so the warning is now dropped only when the design has no clocked cell; the probe matches expect.tsv again (known gaps 19). The other was the personal-path check reading the __pycache__ a direct test run leaves; that folder is never committed and is no longer scanned. The SystemVerilog probes are unchanged: 114 of 148 pass all four stages.
Local candidate · Run builds every lab folder the folder command builds · 15 October
Measured on the students' own folders. 89 lab folders from 94 public CS223 repositories, each on a fresh copy: the folder command (dewfpga bit) against Run on the file a student opens (dewfpga run <file> --build-only, the play button). Before this update the folder command built 13 and Run 2, and every one of the 13 failed on Run. Now Run builds 17: all 13, and 4 the folder command refuses for a reason that is not that design's.
Run reads the folder's pin file. Run used to take only <file>.xdc or the automatic pins, so a lab whose pins are in constraints.xdc or Lab4Const.xdc, for ports named swIns, data or reset, stopped at No default Basys3 pin. Now, as the folder command does, Run takes the one .xdc beside the top that pins the design's ports; one that pins none of them (another lab's) is passed over, and the automatic pins stay for a folder with no such file.
Run on a Vivado project's file builds the project. A file under <name>.srcs/sources_1/new has its pins in constrs_1 and its file list in the .xpr. Run now builds it as the folder command reads the project: its enabled files, its top, its .xdc, and the outputs next to the .xpr. The project command names files from the project folder; Run writes each file:line and the log path out in full, so the editor underlines the right file.
Of the 4 folders only Run builds, 2 hold a file the design does not use that the folder command stops on (a module defined in both a .sv and a .v; an old file that does not compile), and 2 have no .xdc and only board port names.
Tests: two new checks. A folder with constraints.xdc for ports a b y c builds with that file, also beside another lab's .xdc, and a design with board port names and only another lab's .xdc gets the automatic pins. A copy of the Vivado project fixture builds from its source file, the .bit lands next to the .xpr, and an error is named with the source's full path and line. The full suite passed 343 of 343 with 19 known gaps; the SystemVerilog probes are unchanged (114 of 148 pass all four stages).
Local candidate · The course's ready netlist modules build · 15 October
SevSeg_4digit.sv stopped every design that used it. The course hands out its four-digit seven-segment driver as a Vivado netlist. Its port list ends in .dp(\<const0> ), an explicit port: the port dp, whose inside is the net \<const0> (Vivado writes a constant output that way), declared as output \<const0> ;. Vivado builds it; yosys' reader stopped at the . (syntax error, unexpected TOK_ID, expecting '.'), and yosys-slang, the second reader, does not know Vivado's cells (GND, LUT4). In the corpus, 5 lab folders stopped there. The copy the tools read now has dp in the port list and output dp; assign dp = \<const0> ; on the declaration's line, with a note for each line (explicit-port); the student's file stays as it is.
Checked against an independent simulation: the original module, as Vivado means it, under iverilog with the Xilinx cell models, against the netlist the build writes, both driving the same inputs (changed halfway): 600,000 clock cycles, 0 differences, no x, the digit select switched 10 times, dp 0. A netlist with one LUT bit flipped differs within 400,000 cycles, so the comparison is not blind.
Tests: a new check builds a small netlist-form module of the same shape (an explicit constant port, a GND, a LUT2), reads the two notes, keeps the file's bytes, and simulates the netlist: the AND is right on all 4 inputs and dp is 0. It fails on the installed 1.2.0.
An implicit net between two cells was refused as undeclared. Vivado's netlists connect cells with nets they never declare (.Q(direction_motor) on a flip-flop, .I4(direction_motor) on a LUT): an implicit wire, as Verilog allows. The build's first check ran before yosys knew which pin of FDRE is an output, reported the wire as used but has no driver, and the CLI turned that into undeclared-name ... nothing drives it. The course's ready stepper-motor module stopped there. That check now runs on its own, before the synthesis, with the Xilinx cell library read as black boxes (read_verilog -lib -nooverwrite), so a cell's output counts as a driver; a design module named like a cell (INV) stays the student's. The synthesis itself reads the design as before. A first try loaded the library into the synthesis run: the suite caught it, because probe 75's longest path went from 33.61 MHz to 23.02 MHz (the library read without its timing data took that data from the LUT mapping). With the check on its own, probe 75 is at 33.61 MHz again.
Checked the same way: a lab project that uses both ready modules (keypad, stepper motor, seven-segment), its sources under iverilog with the Xilinx cell models against the built netlist, reset then key and start presses: 399,969 compared cycles, 0 differences, no x in the reference (its outputs changed 9 times in that window, so this is a narrow check). The seven-segment comparison was repeated with this change: 0 differences in 600,000 cycles. Two new checks: an implicit net from a flip-flop to a LUT builds (it fails on the installed 1.2.0), and a design module called INV is read by yosys' own reader with one LUT. On the 89 corpus folders, Run now builds 24 (17 before) and the folder command 20 (13 before); every folder the folder command builds, Run builds. The 7 new ones are the course's example projects and a student game that use these ready modules.
Several pin files: the one that pins the top's ports. A Vivado constraint set often holds one .xdc per lab part (cons_decoder2to4.xdc, cons_mux8to1_function.xdc), and Vivado applies them all; a port no design has is skipped with a warning. The folder command used to stop at two-xdc-files unless one was named after the top. Now, when exactly one of them pins any of the top's ports, it uses that one and says so (pins: ... (the one .xdc here that pins mux8to1_function's ports)). When two of them pin the same port (out[3] in one file, out in the other), which pin Vivado keeps is not guessed: the stop stays. Run picks its folder's pin file by the same code. Two new checks: a flat folder with two files (one for another part) builds, the same folder with both pinning y stops, and a Vivado-layout constraint set builds from both the folder command and Run. Both fail on the installed 1.2.0. On the corpus this adds the 2 folders of one student's Lab 3 project: Run builds 26 of the 89 folders, the folder command 22. The full suite passed every product check (342, 19 known gaps; the SystemVerilog probes unchanged, 114 of 148). The one failure was a test's: the SIGINT case of test-json.py waited 15 s for a wrapper that never got the signal, because the suite had been started with run.sh &, and a shell without job control starts a background job with SIGINT ignored, which the wrapper rightly keeps. The test now starts the wrapper with SIGINT at its default; it passes under both launches, and the old test fails the same way when its parent ignores SIGINT.
Local candidate · The folder command needs no pin file for the board's port names · 15 October
⌘⇧B and dewfpga flash stopped where Run did not. A folder with only lab1_top.sv (ports sw, led) built with Run, but the folder command, and with it VS Code's build task, stopped at no-xdc-file: copy Basys3_Master.xdc here and uncomment the pins you use. That is the file a student had to write by hand for lab 1. Now, with no .xdc in the folder and every port of the top named like the board's, the folder command writes <top>.autopins from the synthesized ports with the same code as Run, says pins: automatic Basys3 mapping (no .xdc here, and lab1_top's ports have the board's names), and builds. The pins are byte for byte the ones Run writes for the same file. They follow the design: a new an port gets its pins on the next build, a second build with nothing changed is up to date, and dewfpga clean removes the file. A port without a board name still stops the build, and the message now names those ports (these ports of adder have no board name: a b y). The READMEs say what Run and the folder command read since last night.
Tests: the two checks that expected the stop for board names now expect the build, its pins, up to date and clean; a new check keeps the stop and the named ports for a b y. On the corpus, the folder command now builds 24 of the 89 folders (22 before): the 2 new ones have no .xdc and only board port names. SUITE_C6
Stuck on an error? Every error from this setup, filed under the exact line it printed.