File size: 94,502 Bytes
c5b4972
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73bf55b
c5b4972
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
<title>stcoder-qwen25-7b &mdash; Mikrodev STCoder for PLC Structured Text</title>
<style>
:root{--bg:#e9edf1;--paper:#fff;--paper2:#f4f7f9;--rule:#d3dae1;--ink:#131a21;--mid:#5c6b78;--dim:#8b98a5;
--teal:#0b7d8c;--teal-soft:#d6eef1;--teal-line:#93cfd7;--amber:#9a6512;--amber-soft:#f2e4c9;--amber-line:#d8bb84;
--red:#ac3527;--red-soft:#f6dcd7;--code-bg:#f6f8fa;--code-ink:#1e2831;
--shadow:0 1px 1px rgba(19,26,33,.04),0 8px 26px rgba(19,26,33,.06);}
@media (prefers-color-scheme:dark){:root{--bg:#0b1014;--paper:#131b22;--paper2:#18222a;--rule:#25313b;
--ink:#d5dee6;--mid:#8c9aa7;--dim:#5f6d79;--teal:#3bb6c6;--teal-soft:#10353a;--teal-line:#1d5b65;
--amber:#d09a3f;--amber-soft:#382c12;--amber-line:#6b5322;--red:#e07963;--red-soft:#3d1d17;
--code-bg:#0e161c;--code-ink:#c2ccd6;--shadow:0 1px 1px rgba(0,0,0,.4),0 10px 30px rgba(0,0,0,.34);}}
:root[data-theme=light]{--bg:#e9edf1;--paper:#fff;--paper2:#f4f7f9;--rule:#d3dae1;--ink:#131a21;--mid:#5c6b78;
--dim:#8b98a5;--teal:#0b7d8c;--teal-soft:#d6eef1;--teal-line:#93cfd7;--amber:#9a6512;--amber-soft:#f2e4c9;
--amber-line:#d8bb84;--red:#ac3527;--red-soft:#f6dcd7;--code-bg:#f6f8fa;--code-ink:#1e2831;
--shadow:0 1px 1px rgba(19,26,33,.04),0 8px 26px rgba(19,26,33,.06);}
:root[data-theme=dark]{--bg:#0b1014;--paper:#131b22;--paper2:#18222a;--rule:#25313b;--ink:#d5dee6;--mid:#8c9aa7;
--dim:#5f6d79;--teal:#3bb6c6;--teal-soft:#10353a;--teal-line:#1d5b65;--amber:#d09a3f;--amber-soft:#382c12;
--amber-line:#6b5322;--red:#e07963;--red-soft:#3d1d17;--code-bg:#0e161c;--code-ink:#c2ccd6;
--shadow:0 1px 1px rgba(0,0,0,.4),0 10px 30px rgba(0,0,0,.34);}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--ink);line-height:1.6;-webkit-font-smoothing:antialiased;
font-family:"Segoe UI",system-ui,-apple-system,Roboto,Helvetica,Arial,sans-serif}
.sheet{max-width:1080px;margin:0 auto;padding:clamp(18px,3.5vw,50px) clamp(14px,3.5vw,42px) 90px}
code,pre,.mono{font-family:"Cascadia Code","JetBrains Mono",Consolas,ui-monospace,SFMono-Regular,Menlo,monospace}
.num,table,.bar b{font-variant-numeric:tabular-nums}
.mast{border-top:3px solid var(--ink);padding-top:14px}
.kick{display:flex;flex-wrap:wrap;gap:13px;align-items:baseline;font-family:"Cascadia Code",Consolas,monospace;
font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--mid)}
.kick .id{color:var(--teal);font-weight:700}
h1{font-size:clamp(28px,5vw,47px);line-height:1.03;letter-spacing:-.025em;margin:.3em 0 .16em;font-weight:800;
text-wrap:balance;max-width:24ch}
.sf{font-size:clamp(16px,2vw,19px);color:var(--mid);max-width:66ch;margin:0}
.sf b{color:var(--ink);font-weight:650}
.spec{display:grid;grid-template-columns:repeat(auto-fit,minmax(128px,1fr));gap:1px;background:var(--rule);
border:1px solid var(--rule);border-radius:10px;overflow:hidden;margin:28px 0 0;box-shadow:var(--shadow)}
.spec div{background:var(--paper);padding:12px 14px}
.spec .k{font-family:"Cascadia Code",Consolas,monospace;font-size:10px;letter-spacing:.13em;
text-transform:uppercase;color:var(--dim)}
.spec .v{font-size:19px;font-weight:750;letter-spacing:-.01em;margin-top:3px;word-break:break-word}
.spec .v small{font-size:11.5px;font-weight:500;color:var(--mid);letter-spacing:0;display:block}
section{margin-top:clamp(42px,6vw,70px)}
.sh{display:flex;gap:13px;align-items:baseline;border-bottom:1px solid var(--rule);padding-bottom:9px;margin-bottom:18px}
.sh .sn{font-family:"Cascadia Code",Consolas,monospace;font-size:12px;font-weight:700;color:var(--teal);letter-spacing:.08em}
h2{font-size:clamp(19px,2.6vw,26px);margin:0;letter-spacing:-.015em;font-weight:750;text-wrap:balance}
h3{font-size:16px;margin:24px 0 7px;font-weight:700}
p{max-width:70ch}p.lead{color:var(--mid)}
ul,ol{max-width:72ch;padding-left:20px}li{margin:.3em 0}
.card{background:var(--paper);border:1px solid var(--rule);border-radius:12px;padding:18px 20px;
margin:18px 0;box-shadow:var(--shadow)}
.card.hi{border-left:3px solid var(--teal)}
.card.warn{border-left:3px solid var(--red)}
.card h3{margin-top:0}
.tw{overflow-x:auto;border:1px solid var(--rule);border-radius:11px;box-shadow:var(--shadow);margin:18px 0}
table{border-collapse:collapse;width:100%;background:var(--paper);font-size:13.2px;min-width:600px}
th,td{padding:10px 13px;text-align:right;border-bottom:1px solid var(--rule);vertical-align:top}
th:first-child,td:first-child{text-align:left}
thead th{background:var(--paper2);font-size:10.4px;letter-spacing:.1em;text-transform:uppercase;color:var(--dim);font-weight:700}
tbody tr:last-child td{border-bottom:none}
td.ft{color:var(--teal);font-weight:700}td.bs{color:var(--amber);font-weight:650}
.pick{display:grid;gap:1px;background:var(--rule);border:1px solid var(--rule);border-radius:11px;
overflow:hidden;box-shadow:var(--shadow)}
.pick .row{background:var(--paper);padding:14px 17px;display:grid;grid-template-columns:120px 150px 1fr;gap:14px;align-items:start}
@media(max-width:700px){.pick .row{grid-template-columns:1fr;gap:5px}}
.pick .vram{font-weight:750;font-size:15px}
.pick .q{font-family:"Cascadia Code",Consolas,monospace;font-size:12.6px;font-weight:700;color:var(--teal)}
.pick .q small{display:block;color:var(--mid);font-weight:400;font-size:11px}
.pick .why{color:var(--mid);font-size:13.5px}
.pick .why em{color:var(--amber);font-style:normal;font-weight:650}
.steps{counter-reset:s;list-style:none;padding:0}
.steps li{counter-increment:s;position:relative;padding-left:38px;margin-bottom:16px}
.steps li::before{content:counter(s);position:absolute;left:0;top:0;width:25px;height:25px;border-radius:50%;
background:var(--teal-soft);color:var(--teal);font-family:"Cascadia Code",Consolas,monospace;font-size:12px;
font-weight:700;display:flex;align-items:center;justify-content:center}
.steps b{display:block;font-size:14.5px}
.steps p{margin:.2em 0;color:var(--mid);font-size:13.5px}
pre{margin:9px 0 0;padding:12px 14px;overflow-x:auto;background:var(--code-bg);color:var(--code-ink);
font-size:12.3px;line-height:1.55;border-radius:8px;border:1px solid var(--rule)}
pre.plain{border-radius:0;border:none;margin:0}
pre .bad{background:var(--red-soft);color:var(--red);font-weight:700;border-radius:3px;padding:0 2px}
pre .good{background:var(--teal-soft);color:var(--teal);font-weight:700;border-radius:3px;padding:0 2px}
.pgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(292px,1fr));gap:13px}
.pc{background:var(--paper);border:1px solid var(--rule);border-radius:11px;padding:14px 16px;box-shadow:var(--shadow)}
.pc .q{font-size:14px;font-weight:650;margin-bottom:6px}
.pc .q::before{content:"\201C"}.pc .q::after{content:"\201D"}
.pc .e{font-size:13px;color:var(--mid)}
.pc .n{font-size:12.2px;color:var(--dim);margin-top:6px;font-style:italic}
.fig{background:var(--paper);border:1px solid var(--rule);border-radius:12px;padding:19px 21px 15px;
margin:20px 0;box-shadow:var(--shadow)}
.fc{font-family:"Cascadia Code",Consolas,monospace;font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;
color:var(--dim);margin-bottom:4px}
.ftl{font-weight:700;font-size:15px;margin-bottom:13px}
.legend{display:flex;flex-wrap:wrap;gap:15px;font-size:12.3px;color:var(--mid);margin:0 0 14px}
.legend span{display:inline-flex;align-items:center;gap:6px}
.sw{width:11px;height:11px;border-radius:2px}.sw.b{background:var(--amber)}.sw.f{background:var(--teal)}
.sw.r{background:var(--red)}
.frow{display:grid;grid-template-columns:180px 1fr;gap:13px;align-items:center;margin-bottom:14px}
@media(max-width:640px){.frow{grid-template-columns:1fr;gap:5px}}
.flab{font-size:13px;font-weight:650}
.flab small{display:block;font-weight:400;color:var(--mid);font-size:10.6px;font-family:"Cascadia Code",Consolas,monospace}
.fbars{display:flex;flex-direction:column;gap:5px}
.bar{position:relative;height:23px;background:var(--paper2);border:1px solid var(--rule);border-radius:5px;overflow:hidden}
.bar>i{position:absolute;inset:0 auto 0 0;display:block}
.bar.base>i{background:var(--amber)}.bar.base2>i{background:var(--amber);opacity:.6}
.bar.ft>i{background:var(--teal)}.bar.ft2>i{background:var(--teal);opacity:.6}
.bar>b{position:absolute;right:8px;top:0;height:23px;display:flex;align-items:center;font-size:11.4px;
font-weight:700;color:var(--ink)}
.strip{display:flex;gap:2px;height:26px}
.strip i{flex:1 1 0;border-radius:1.5px;min-width:3px}
.strip i.ok{background:var(--teal)}.strip i.vi{background:var(--amber)}.strip i.nc{background:var(--red)}
.diff{display:grid;grid-template-columns:1fr 1fr;gap:14px}
@media(max-width:820px){.diff{grid-template-columns:1fr}}
.panel{background:var(--paper);border:1px solid var(--rule);border-radius:11px;overflow:hidden;box-shadow:var(--shadow)}
.panel.base{border-top:3px solid var(--amber-line)}.panel.ft{border-top:3px solid var(--teal-line)}
.ph{display:flex;justify-content:space-between;align-items:center;gap:8px;padding:9px 13px;border-bottom:1px solid var(--rule)}
.tag{font-family:"Cascadia Code",Consolas,monospace;font-size:10.3px;letter-spacing:.12em;text-transform:uppercase;font-weight:700}
.tag.base{color:var(--amber)}.tag.ft{color:var(--teal)}
.badge{font-family:"Cascadia Code",Consolas,monospace;font-size:10.7px;font-weight:700;padding:3px 8px;
border-radius:99px;white-space:nowrap}
.badge.ok{background:var(--teal-soft);color:var(--teal)}.badge.warn{background:var(--amber-soft);color:var(--amber)}
.pv{display:flex;flex-wrap:wrap;gap:5px;padding:9px 13px;border-top:1px solid var(--rule)}
.pv code{font-size:10.5px;padding:2px 7px;border-radius:99px}
.pv code.v{background:var(--red-soft);color:var(--red)}.pv code.c{background:var(--teal-soft);color:var(--teal)}
.note{font-size:13.2px;color:var(--mid);border-left:2px solid var(--rule);padding-left:13px;margin:12px 0 0;max-width:82ch}
.note b{color:var(--ink);font-weight:650}
.chip{display:inline-block;font-family:"Cascadia Code",Consolas,monospace;font-size:10.3px;font-weight:700;
padding:2px 7px;border-radius:99px}
.chip.ok{background:var(--teal-soft);color:var(--teal)}.chip.vi{background:var(--amber-soft);color:var(--amber)}
.chip.nc{background:var(--red-soft);color:var(--red)}
.sib{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:11px;margin-top:14px}
.sib a{display:block;background:var(--paper);border:1px solid var(--rule);border-radius:10px;padding:12px 14px;
text-decoration:none;color:inherit;box-shadow:var(--shadow)}
.sib a.self{border-color:var(--teal);background:var(--teal-soft)}
.sib .sn2{font-family:"Cascadia Code",Consolas,monospace;font-size:11.6px;font-weight:700;color:var(--teal)}
.sib .sd{font-size:12.6px;color:var(--mid);margin-top:2px}
footer{margin-top:60px;padding-top:19px;border-top:1px solid var(--rule);font-size:12.4px;color:var(--dim);max-width:82ch}
footer code{color:var(--mid);font-size:11.6px}
</style>
<div class="sheet">

  <div class="mast">
    <div class="kick"><span class="id">Mikrodev/stcoder-qwen25-7b-gguf</span><span>Fine-tuned from Qwen/Qwen2.5-Coder-7B-Instruct</span>
      <span>apache-2.0</span><span>GGUF &middot; Ollama / llama.cpp</span></div>
    <h1>stcoder-qwen25-7b</h1>
    <p class="sf">stcoder-qwen25-7b is a chat-only model fine-tuned from Qwen2.5-Coder-7B-Instruct that writes IEC 61131-3 Structured Text in the Mikrodev LogicStudio dialect. It is the default recommendation of this four-model line, at Q8_0 (7.54 GiB): in the head-to-head study it was the fastest per answer of the four (mean 5.8 s per reply with a short system prompt, 5.1 s with the full rule book) and it had the highest ChrF of the models that delivered all 15 replies (38.5).</p>
    <p class="sf" style="font-size:15px;margin-top:10px">This is a fine-tune for the Structured Text coding rules of Advance Logic Builder (ALB), the IEC 61131-3 engineering environment developed by Mikrodev and shipped as Mikrodev LogicStudio. It follows Mikrodev&#x27;s ST dialect, not generic IEC 61131-3 and not another vendor&#x27;s conventions.</p>
  </div>

  <div class="spec">
    <div><div class="k">Base model</div><div class="v" style="font-size:14px">Qwen2.5-Coder-7B-Instruct<small>Qwen/Qwen2.5-Coder-7B-Instruct</small></div></div>
    <div><div class="k">Builds</div><div class="v">3<small>Q4_K_M / Q6_K / Q8_0</small></div></div>
    <div><div class="k">Smallest</div><div class="v">4.4 GiB<small>Q4_K_M</small></div></div>
    <div><div class="k">Recommended</div><div class="v" style="color:var(--teal)">Q8_0<small>7.54 GiB</small></div></div>
    <div><div class="k">Dialect-clean</div><div class="v" style="color:var(--teal)">93.3%<small>base: 13.3%</small></div></div>
    <div><div class="k">Tool calling</div><div class="v" style="color:var(--red);font-size:15px">Not supported<small>chat only</small></div></div>
  </div>

  <!-- 1. PICK -->
  <section>
    <div class="sh"><span class="sn">1</span><h2>Which build should you download?</h2></div>
    <p class="lead">Start from the graphics memory you have. Every build below is the same fine-tune;
    they differ only in how heavily the weights are compressed.</p>
    <div class="pick"><div class="row"><div class="vram">9.5 GiB free VRAM</div><div class="q">Q8_0<small>qwen25_coder_7b-tc.q8_0.gguf &middot; 7.54 GiB</small></div><div class="why">recommended &mdash; the build this study ran</div></div><div class="row"><div class="vram">7.8 GiB free VRAM</div><div class="q">Q6_K<small>qwen25_coder_7b-tc.q6_k.gguf &middot; 5.82 GiB</small></div><div class="why">fine</div></div><div class="row"><div class="vram">6.4 GiB free VRAM</div><div class="q">Q4_K_M<small>qwen25_coder_7b-tc.q4_k_m.gguf &middot; 4.36 GiB</small></div><div class="why">not recommended</div></div></div>
    <div class="card hi"><h3>Short answer</h3>
      <p>Download <b>Q8_0</b> (<code>qwen25_coder_7b-tc.q8_0.gguf</code>, 7.54 GiB).
      Q8_0 is the smallest build with no practically measurable loss against the trained weights - it is the precision we recommend for real work. Free VRAM needed: about
      9.5 GiB &mdash; the file plus the 8192-token context and runtime.</p></div>
    <div class="card warn"><h3>Why not Q4_K_M</h3>
      <p>Q4_K_M halves the bits per weight again. The only capability we measured across quantisations degraded sharply - tool-call composite 0.816 at Q8_0 against 0.642 at Q4_K_M, measured on the Qwen3.5-9B family, the only one of the four where tool-calling worked at all - and low-bit quantisation is a known source of drift and hallucination on long or unusual requests. Our 15-prompt code study never ran at Q4, so we have no measured code-quality figure for it: treat this as a precaution, not a measured code-quality gap.</p></div>
    <p class="note"><b>Also published for this model.</b> The LoRA adapter it was trained as
    (<code>lora_adapter/</code> in the same repository) so you can merge it at any
    precision or continue training from it, and the f16 merged weights
    (f16 is the merged fine-tune at full precision: the reference build, for evaluation or for producing your own quantisations.). Ready-to-run Ollama <code>Modelfile</code>s ship next to every build:
    correct chat template, stop tokens, <code>num_ctx 8192</code> &mdash; the sequence length this
    model was fine-tuned at &mdash; and the system prompt.</p>
    <div class="tw"><table>
      <thead><tr><th>File</th><th>Quantisation</th><th>Size</th><th>Fit on 16 GB</th><th style="text-align:left">Note</th></tr></thead>
      <tbody><tr><td><code>qwen25_coder_7b-tc.q4_k_m.gguf</code></td><td class="num">Q4_K_M</td><td class="num">4.36 GiB</td><td class="num">solid</td><td></td></tr><tr><td><code>qwen25_coder_7b-tc.q6_k.gguf</code></td><td class="num">Q6_K</td><td class="num">5.82 GiB</td><td class="num">solid</td><td></td></tr><tr><td><code>qwen25_coder_7b-tc.q8_0.gguf</code></td><td class="num">Q8_0</td><td class="num">7.54 GiB</td><td class="num">solid</td><td>tested in this study</td></tr></tbody>
    </table></div>
  </section>

  <!-- 2. WHO -->
  <section>
    <div class="sh"><span class="sn">2</span><h2>Is this the right model for you?</h2></div>
    <p>Start here unless you have a specific reason not to. First, the boundary: this is a chat model. You describe a plant requirement, it replies with a Structured Text body. Tool calling and function calling were trained and evaluated for this line, but the capability was not usable across the four models, so it was dropped as a product decision and these builds are published and supported as chat-only models. There are no tool or function calls, no JSON actions, no MCP schemas.

Head-to-head standing, minimal (short) system prompt, this study, ChrF averaged over delivered replies so the denominator is identical for all four: stcoder-qwen25-7b 93.3% rule-clean, 100% delivery (15/15), ChrF 38.5, composite 0.844, mean 340 output tokens, 5.8 s per reply. Its actual standing is: highest ChrF among the models that delivered every reply (38.5; stcoder-qwen35-9b scored 40.9 but delivered 14 of 15); joint second on clean rate with stcoder-qwen35-9b (both 93.3%), behind stcoder-qwen25-14b&#x27;s 100.0%; composite 0.844, which is inside the noise band with stcoder-qwen35-9b (0.845) and stcoder-qwen25-14b (0.842); fastest per answer of the four.

Which one should I download? Default: stcoder-qwen25-7b at Q6_K (5.82 GiB), or Q4_K_M (4.36 GiB) on an 8 GB card, because it is the fastest per answer, the smallest to run, and at the top of the head-to-head on ChrF at full delivery. Choose stcoder-qwen25-14b at Q6_K (11.29 GiB) only on 16 GB or more, if you want the one model that was 100% rule-clean in both prompt conditions, accepting the lowest head-to-head ChrF (34.9) and roughly half the generation rate per token (about 50 tok/s against this model&#x27;s 103.6). Choose stcoder-qwen35-9b only if you want the reasoning family, accepting about 3.6x the output tokens per answer (1227 against 340) and one reply in fifteen that spent its entire 8,192-token budget on reasoning and returned no code at all after 99 seconds, which is why it is the slowest per answer at 17.2 s. Choose stcoder-gemma4-12b only for non-Qwen lineage diversity; it is the weakest fine-tuning case in this study (80.0% clean in the full condition against its fully-prompted base&#x27;s 73.3%, and its base scored a higher mean R24 than the fine-tune in that condition).

At matched quantisation this model is roughly half the size of the 14B, not a third: 7B Q6_K 5.82 GiB against 14B Q6_K 11.29 GiB. Three disclosures apply to all four cards: no tool calling in this release; compile every block in LogicStudio before deployment; and quantisation was not controlled in the head-to-head (fine-tunes ran at Q8_0 or Q6_K, the stock bases at whatever the Ollama library default is).</p>
    <div class="sib"><a class="self" href="https://huggingface.co/Mikrodev/stcoder-qwen25-7b-gguf"><div class="sn2">Mikrodev/stcoder-qwen25-7b-gguf</div><div class="sd">7B &middot; default pick, fastest per answer &middot; you are here</div></a><a class="" href="https://huggingface.co/Mikrodev/stcoder-qwen25-14b-gguf"><div class="sn2">Mikrodev/stcoder-qwen25-14b-gguf</div><div class="sd">14B &middot; only one 100% clean in both conditions</div></a><a class="" href="https://huggingface.co/Mikrodev/stcoder-qwen35-9b-gguf"><div class="sn2">Mikrodev/stcoder-qwen35-9b-gguf</div><div class="sd">9B &middot; reasoning family, highest ChrF when it answers</div></a><a class="" href="https://huggingface.co/Mikrodev/stcoder-gemma4-12b-gguf"><div class="sn2">Mikrodev/stcoder-gemma4-12b-gguf</div><div class="sd">12B &middot; non-Qwen lineage, weakest fine-tune case</div></a></div>

    <div class="fig">
      <div class="fc">Table &mdash; the four models on identical measurements</div>
      <div class="ftl">Same 15 requests, realistic prompt, greedy decoding. ChrF is averaged over
      <i>delivered</i> replies so the denominator is the same for every model.</div>
      <div class="tw"><table>
        <thead><tr><th>Model</th><th>Clean</th><th>Delivered</th><th>ChrF</th><th>Composite</th>
          <th>Out tok</th><th>Per reply</th><th>Recommended build</th></tr></thead>
        <tbody><tr><td><code>stcoder-qwen25-7b</code> (this model)<br><span style="color:var(--dim);font-size:11.5px">7B &middot; default pick, fastest per answer</span></td><td class="num ft">93.3%</td><td class="num">15/15</td><td class="num">38.5</td><td class="num">0.844</td><td class="num">340</td><td class="num">5.8s</td><td class="num">Q8_0 &middot; 7.54 GiB</td></tr><tr><td><code>stcoder-qwen25-14b</code><br><span style="color:var(--dim);font-size:11.5px">14B &middot; only one 100% clean in both conditions</span></td><td class="num">100.0%</td><td class="num">15/15</td><td class="num">34.9</td><td class="num">0.842</td><td class="num">288</td><td class="num">8.6s</td><td class="num">Q8_0 &middot; 14.62 GiB</td></tr><tr><td><code>stcoder-qwen35-9b</code><br><span style="color:var(--dim);font-size:11.5px">9B &middot; reasoning family, highest ChrF when it answers</span></td><td class="num">93.3%</td><td class="num">14/15</td><td class="num">40.9</td><td class="num">0.845</td><td class="num">1227</td><td class="num">17.2s</td><td class="num">Q8_0 &middot; 9.11 GiB</td></tr><tr><td><code>stcoder-gemma4-12b</code><br><span style="color:var(--dim);font-size:11.5px">12B &middot; non-Qwen lineage, weakest fine-tune case</span></td><td class="num">86.7%</td><td class="num">15/15</td><td class="num">35.9</td><td class="num">0.83</td><td class="num">401</td><td class="num">8.5s</td><td class="num">Q8_0 &middot; 11.80 GiB</td></tr></tbody>
      </table></div>
      <p class="note"><b>How to read this.</b> The 14B has the highest clean rate (100%). The 9B has
      the highest ChrF among delivered replies (40.9) but delivered on only 14 of 15, so at full
      delivery the 7B leads (38.5). Composites for the 9B, 7B and 14B (0.845 / 0.844 / 0.842) are
      within noise of each other. Sizes are <b>GiB</b> throughout.
      <b>If you are undecided, take stcoder-qwen25-7b at Q8_0.</b></p>
    </div>

    <div class="card warn"><h3>Read this before you start</h3>
      <ul>
        <li><b>Chat only &mdash; no tool calling.</b> You describe what you need and the model writes
        Structured Text. Tool calling was trained and evaluated for this line: the Qwen3.5-9B family
        did learn it (tool-call composite 0.816 at Q8_0, falling to 0.642 at Q4_K_M) while the
        code-focused families scored 0.02&ndash;0.06 and the Gemma family exactly 0.00. Because it was
        not usable across the line, <b>tool calling was dropped as a product decision</b> and these
        builds are published and supported as chat models. Do not build an agent on them.</li>
        <li><b>Always compile before deployment.</b> The model produces dialect-correct code, which is
        not the same as correct control logic. Compile it in LogicStudio and review the logic.</li>
        <li><b>It is a small local model.</b> Expect to iterate on a prompt, not to receive a finished
        project.</li>
      </ul>
    </div>
  </section>

  <!-- 3. SETUP -->
  <section>
    <div class="sh"><span class="sn">3</span><h2>Set-up, step by step</h2></div>
    <ol class="steps"><li><b>Install Ollama</b><p>Ollama 0.3 or later. llama.cpp works equally well if you prefer it; the GGUF files are standard.</p><pre>winget install Ollama.Ollama   # or: brew install ollama</pre></li><li><b>Download one GGUF file</b><p>Take Q6_K for a 12 GB card, Q4_K_M for 8 GB, Q8_0 for 16 GB or more. You need exactly one file, not the whole repository.</p><pre>hf download Mikrodev/stcoder-qwen25-7b-gguf qwen25_coder_7b-tc.q6_k.gguf --local-dir .</pre></li><li><b>Write a Modelfile next to the GGUF</b><p>Keep the system prompt short. In this study the short &#x27;minimal&#x27; system prompt gave a higher ChrF (38.5) than pasting the entire rule book (35.3) - the dialect is in the weights, and restating it adds nothing. Greedy decoding matches how the model was measured.</p><pre>cat &gt; Modelfile &lt;&lt;&#x27;EOF&#x27;
FROM ./qwen25_coder_7b-tc.q6_k.gguf
PARAMETER temperature 0
PARAMETER top_p 1
PARAMETER num_ctx 8192
# the published numbers were measured with num_ctx 16384 / num_predict 8192
# num_predict 2048 is a practical local default (longest reply in the study: 708 tokens); raise it to 8192 to reproduce them or if a long block truncates
PARAMETER num_predict 2048
SYSTEM &quot;You are a PLC programming assistant for the Mikrodev LogicStudio IDE. You write IEC 61131-3 Structured Text for Mikrodev controllers. Reply with the code body only.&quot;
EOF</pre></li><li><b>Create the Ollama model</b><p>Names the model locally. Use a tag that records the quant so you can keep more than one. For reference, the tags actually run in the study were stcoder_qwen25-7b:q8 (fine-tune) and qwen2.5-coder:7b (stock base); the sibling arms were stcoder_qwen25-14b:q6, stcoder_qwen35-9b:q8, stcoder_gemma4-12b:q6 against qwen2.5-coder:14b, qwen3.5:9b and gemma4:12b.</p><pre>ollama create stcoder-qwen25-7b:q6_k -f Modelfile</pre></li><li><b>Ask it for a block</b><p>Describe the plant requirement in plain language, as you would to a colleague. Do not list dialect rules.</p><pre>ollama run stcoder-qwen25-7b:q6_k &quot;Silo inlet: on a fill request with the high-level switch clear, open the inlet valve; 3 seconds after the valve open-confirm is made, start the blower. Any fault stops the blower at once and closes the valve 2 seconds later.&quot;</pre></li><li><b>Paste into LogicStudio and compile</b><p>The model returns a code body - variable blocks plus statements, no PROGRAM or FUNCTION_BLOCK wrapper, because the IDE owns the shell. Compile before you go anywhere near a controller. In the 15-prompt study the fine-tune broke no dialect rule on 14 of 15 prompts, but rule-clean is not the same as logically correct: the study found an undeclared enumeration member, an unused constant and a reset branch that counted up instead of clearing.</p></li></ol>
  </section>

  <!-- 4. PROMPTS -->
  <section>
    <div class="sh"><span class="sn">4</span><h2>What to ask it first</h2></div>
    <p class="lead">These are ordinary single-purpose requests, the kind this model handles well.
    Type them as they are.</p>
    <div class="pgrid"><div class="pc"><div class="q">Silo inlet sequence: when the fill request is on and the high-level switch is clear, open the inlet valve. Three seconds after the valve open-confirm is made, start the blower. Any fault stops the blower immediately and closes the valve two seconds later.</div><div class="e">A code body with VAR_INPUT / VAR_OUTPUT blocks, two TON instances called positionally, and VAR CONSTANT presets in milliseconds (BLOWER_DELAY_MS : INT := 3000, VALVE_CLOSE_MS : INT := 2000). No T#3s, no named parameters, no PROGRAM wrapper.</div><div class="n">Fresh prompt, not part of the 15-prompt study - written to exercise the same sequencing behaviour without being read as independent evidence. On the study&#x27;s equivalent timer prompts the fine-tune was rule-clean (R24 100) where the base emitted TIME literals in named-parameter calls.</div></div><div class="pc"><div class="q">Chiller circulation pump: the pump follows the compressor and keeps running for 45 seconds after the compressor stops so the plate exchanger cannot freeze. E-stop cuts both at once. Give me a post-circulation flag for the HMI.</div><div class="e">One TON used as an off-delay with PURGE_MS : INT := 45000, plus a separate boolean for the HMI flag.</div><div class="n">Fresh prompt. Building an off-delay from an on-delay is the pattern the study measured on a duct-purge variant, where the fine-tune was rule-clean (R24 100, ChrF 38.41) and the base used a TIME literal with named parameters.</div></div><div class="pc"><div class="q">Screw feeder motor: NO start pushbutton, NC stop pushbutton, motor overload aux contact. Normal seal-in, and the overload trip latches until the stop button is pressed.</div><div class="e">A seal-in latch built from R_TRIG edge instances plus a trip latch, driving one boolean motor output.</div><div class="n">Fresh prompt. The study&#x27;s equivalent seal-in prompt was rule-clean (R24 100, ChrF 42.67) but is also where the model declared a HOLD_MS constant it never used and wrote a comment describing a 500 ms hold the code does not implement - so check the declarations against the body on this shape of request.</div></div><div class="pc"><div class="q">Level transmitter, 4-20 mA on a 0-8 m tank: 6400 counts = 4 mA and 31900 counts = 20 mA. Convert the raw integer to metres, clamp to 0-8, and raise a wire-break flag below 4 mA.</div><div class="e">REAL arithmetic with named constants for the raw endpoints and the engineering span, an explicit clamp written with IF statements rather than LIMIT, and a wire-break boolean.</div><div class="n">Fresh prompt with different endpoints from the study&#x27;s pressure version. State both raw endpoints explicitly: on the study&#x27;s open-ended scaling-helper prompt the model reused the prompt&#x27;s wire-break threshold (3200 counts) as the zero point of its 4-20 mA scaling instead of the 4 mA endpoint the prompt supplied.</div></div><div class="pc"><div class="q">Operators enter a drying oven setpoint on the HMI. Clamp it to 55.0-120.0 degC, raise an out-of-range warning the operator can acknowledge, and ramp the setpoint that goes to the loop at 1.5 degC per second.</div><div class="e">VAR CONSTANT limits and a RAMP_STEP, a clamp written with IF/ELSIF, and a per-scan ramp step towards the target.</div><div class="n">Fresh prompt. The study&#x27;s hot-water equivalent was this model&#x27;s strongest single row (ChrF 55.07 for the fine-tune against 38.00 for the base, both rule-clean); treat that as evidence about the pattern, not about this prompt.</div></div><div class="pc"><div class="q">Drying oven element bank: average the load current over the last 10 samples, then compute element power as current squared times 3.2 ohm. If power stays above 1800 W for 4 seconds, latch an overload bit that the reset pushbutton clears.</div><div class="e">A 1-D ARRAY or a running-average variable, multiplication instead of the ** operator, a TON with an INT millisecond preset, and a latch cleared by the reset input.</div><div class="n">Fresh prompt. On the study&#x27;s heater-panel equivalent the fine-tune&#x27;s R24 rose from the base&#x27;s 88.24 to 100 and ChrF from 36.30 to 43.37; the base produced a PROGRAM wrapper with a TIME literal, the fine-tune a body-only block with positional timer calls.</div></div><div class="pc"><div class="q">A photo eye on the palletizer pulses once per case. Give me a lifetime total for maintenance records and a shift total the supervisor clears with a reset button.</div><div class="e">An R_TRIG on the photo eye and two DINT counters, one of which is cleared - not incremented - by the reset input.</div><div class="n">Fresh prompt. The study&#x27;s bottle-counter equivalent is the row where rule-clean output (R24 100) still had the wrong logic: the model incremented the shift counter while the reset was active instead of zeroing it. A good first prompt precisely because that defect is easy to spot and fix.</div></div><div class="pc"><div class="q">We poll a 16-bit status word from a soft starter over Modbus TCP. In decimal, 1 = ready, 2 = running, 8 = fault, 128 = at speed, and bits 8 to 11 hold a warning code. Break the word out into booleans, pull the warning code out as an integer 0-15, and give me one drive-healthy flag.</div><div class="e">Bit tests against decimal constants or bit indices, four or five booleans, an integer warning code, and a single healthy flag.</div><div class="n">Fresh prompt, and deliberately written in decimal. The study&#x27;s version quoted the drive manual&#x27;s hex masks (16#0001, 16#0002, 16#0008, 16#0080, 16#0F00) and the model copied them verbatim - the one rule violation it committed in its minimal arm. Convert to decimal or bit indices before you paste.</div></div></div>
    <ul><li>Keep the system prompt short. The dialect is in the weights: with no rules listed the model scored R24 99.80 and ChrF 38.5; with the whole rule book pasted in, R24 reached 100.0 but ChrF fell to 35.3. A one-line role description is enough.</li><li>Describe the plant, not the code. Every prompt in the study was written the way an engineer talks - &#x27;the fan should keep running for 30 seconds after the motor is switched off so the duct clears&#x27;. None mentioned a dialect rule, and the model still produced INT millisecond presets and positional timer calls.</li><li>Convert hex before you paste. If a drive or device manual gives masks as 16#0F00, translate them to decimal or to bit indices in your prompt. Quoting hex is what pulled a forbidden literal into the output on the one prompt where this model broke a rule in its minimal arm.</li><li>Run greedy. The reported numbers are temperature 0, single seed. Raising temperature buys variety you do not want in control code.</li><li>Compile first, read second, deploy third. Then read the declarations specifically: check that every VAR CONSTANT is actually used, that every enumeration member referenced in a CASE is declared, and that reset branches clear rather than increment. Those are the three defects found in rule-clean output.</li><li>Ignore the paragraph after the code block. Roughly 1 in 15 replies adds explanatory prose, and in one measured case that prose contradicted the code it described.</li><li>Ask for one block at a time. The model is at its best on a single well-bounded requirement - a timer, a latch, an interlock, a scaling calculation - and its weakest ChrF rows were the most open-ended structural prompts.</li><li>Iterate by naming the defect, not by re-listing rules. &#x27;The shift counter increments on reset instead of clearing - fix that branch&#x27; works; re-pasting the dialect rules does not improve output.</li><li>It has no tools and no memory of your project. Give it the signal names you want in the prompt, or expect it to invent its own (bStartBtn, rPressureBar and so on - consistent Hungarian-style prefixes, but its own choices).</li></ul>
  </section>

  <!-- 5. EVIDENCE -->
  <section>
    <div class="sh"><span class="sn">5</span><h2>What fine-tuning changed, measured</h2></div>
    <p class="lead">The same 15 requests were sent to the stock base model
    (<code>qwen2.5-coder:7b</code>) and to this fine-tune, twice: once with a short realistic
    system prompt that lists <b>no dialect rules</b>, and once with the entire rule book pasted in.
    Greedy decoding, fixed seed, identical prompts.</p>

    <div class="fig">
      <div class="fc">Figure 1 &mdash; dialect-clean replies out of 15</div>
      <div class="ftl">Clean means: code was delivered and it broke no dialect rule</div>
      <div class="legend"><span><i class="sw b"></i>base</span><span><i class="sw f"></i>this fine-tune</span></div>
      <div class="frow"><div class="flab">No rules in prompt<small>realistic use</small></div>
        <div class="fbars"><div class="bar base"><i style="width:13.3%"></i><b>13.3%</b></div><div class="bar ft"><i style="width:93.3%"></i><b>93.3%</b></div></div></div>
      <div class="frow"><div class="flab">Full rule book in prompt<small>charitable to base</small></div>
        <div class="fbars"><div class="bar base2"><i style="width:33.3%"></i><b>33.3%</b></div><div class="bar ft2"><i style="width:100.0%"></i><b>100.0%</b></div></div></div>
    </div>

    <div class="fig">
      <div class="fc">Figure 2 &mdash; per-prompt outcome, no rules in prompt</div>
      <div class="ftl">One tick per test prompt, same order in both rows</div>
      <div class="legend"><span><i class="sw f"></i>clean</span><span><i class="sw b"></i>rule violation</span>
        <span><i class="sw r"></i>no code delivered</span></div>
      <div class="frow"><div class="flab">Base</div><div class="strip"><i class="ok"></i><i class="ok"></i><i class="vi"></i><i class="vi"></i><i class="vi"></i><i class="vi"></i><i class="vi"></i><i class="vi"></i><i class="vi"></i><i class="vi"></i><i class="vi"></i><i class="vi"></i><i class="vi"></i><i class="vi"></i><i class="vi"></i></div></div>
      <div class="frow"><div class="flab">Fine-tune</div><div class="strip"><i class="ok"></i><i class="ok"></i><i class="ok"></i><i class="ok"></i><i class="ok"></i><i class="ok"></i><i class="ok"></i><i class="vi"></i><i class="ok"></i><i class="ok"></i><i class="ok"></i><i class="ok"></i><i class="ok"></i><i class="ok"></i><i class="ok"></i></div></div>
    </div>

    <div class="tw"><table>
      <thead><tr><th>Measure</th><th>Base</th><th>This fine-tune</th><th>Base + full rules</th><th>Fine-tune + full rules</th></tr></thead>
      <tbody>
        <tr><td>Dialect-clean replies</td><td class="num bs">13.3%</td><td class="num ft">93.3%</td><td class="num">33.3%</td><td class="num">100.0%</td></tr>
        <tr><td>Delivered code at all</td><td class="num">100.0%</td><td class="num">100.0%</td><td class="num">100.0%</td><td class="num">100.0%</td></tr>
        <tr><td>R24 rule compliance</td><td class="num">93.34</td><td class="num">99.8</td><td class="num">97.16</td><td class="num">100.0</td></tr>
        <tr><td>ChrF vs reference code</td><td class="num">32.4</td><td class="num">38.5</td><td class="num">29.5</td><td class="num">35.3</td></tr>
        <tr><td>Output tokens (mean &middot; median)</td><td class="num">728 &middot; 680</td><td class="num">340 &middot; 269</td><td class="num">670 &middot; 621</td><td class="num">304 &middot; 254</td></tr>
        <tr><td>Time per reply</td><td class="num">7.2s</td><td class="num">5.8s</td><td class="num">6.6s</td><td class="num">5.1s</td></tr>
        <tr><td>Throughput</td><td class="num">154.1 t/s</td><td class="num">103.6 t/s</td><td class="num">153.0 t/s</td><td class="num">104.4 t/s</td></tr>
        <tr><td>Opened with prose, not code</td><td class="num">100.0%</td><td class="num">6.7%</td><td class="num">100.0%</td><td class="num">6.7%</td></tr>
      </tbody>
    </table></div>
    <p class="note"><b>On throughput:</b> the fine-tune is served here at higher precision than the
    stock base model (Ollama's default build is more heavily compressed), so it generates more slowly
    per token. Quantisation was not matched in this comparison &mdash; see the limitations.</p>
  </section>

  <!-- 6. VIOLATIONS -->
  <section>
    <div class="sh"><span class="sn">6</span><h2>Which dialect rules the base model breaks</h2></div>
    <p class="lead">Counted over replies that actually contained code. The upper bar is the realistic
    condition; the lower, lighter bar is with the full rule book in the system prompt &mdash; the
    difference shows which habits an instruction can fix and which it cannot.</p>
    <div class="fig">
      <div class="fc">Figure 3 &mdash; base-model rule violations, qwen2.5-coder:7b &mdash;
        all 11 rules it broke</div>
      <div class="legend" style="margin-bottom:10px"><span style="color:var(--dim)">bar length = share
        of delivered replies that broke the rule, full width = 100%</span></div>
      <div class="frow"><div class="flab"><code>no_named_param</code><small>minimal &middot; full rules</small></div><div class="fbars"><div class="bar base"><i style="width:46.7%"></i><b>7/15</b></div><div class="bar base2"><i style="width:46.7%"></i><b>7/15</b></div></div></div><div class="frow"><div class="flab"><code>no_TIME_literal</code><small>minimal &middot; full rules</small></div><div class="fbars"><div class="bar base"><i style="width:46.7%"></i><b>7/15</b></div><div class="bar base2"><i style="width:0.7%"></i><b>0/15</b></div></div></div><div class="frow"><div class="flab"><code>no_pou_program</code><small>minimal &middot; full rules</small></div><div class="fbars"><div class="bar base"><i style="width:33.3%"></i><b>5/15</b></div><div class="bar base2"><i style="width:0.7%"></i><b>0/15</b></div></div></div><div class="frow"><div class="flab"><code>no_pou_fb</code><small>minimal &middot; full rules</small></div><div class="fbars"><div class="bar base"><i style="width:13.3%"></i><b>2/15</b></div><div class="bar base2"><i style="width:6.7%"></i><b>1/15</b></div></div></div><div class="frow"><div class="flab"><code>no_based_literal</code><small>minimal &middot; full rules</small></div><div class="fbars"><div class="bar base"><i style="width:6.7%"></i><b>1/15</b></div><div class="bar base2"><i style="width:6.7%"></i><b>1/15</b></div></div></div><div class="frow"><div class="flab"><code>no_struct</code><small>minimal &middot; full rules</small></div><div class="fbars"><div class="bar base"><i style="width:6.7%"></i><b>1/15</b></div><div class="bar base2"><i style="width:6.7%"></i><b>1/15</b></div></div></div><div class="frow"><div class="flab"><code>no_single_quote</code><small>minimal &middot; full rules</small></div><div class="fbars"><div class="bar base"><i style="width:6.7%"></i><b>1/15</b></div><div class="bar base2"><i style="width:0.7%"></i><b>0/15</b></div></div></div><div class="frow"><div class="flab"><code>no_var_persistent</code><small>minimal &middot; full rules</small></div><div class="fbars"><div class="bar base"><i style="width:0.7%"></i><b>0/15</b></div><div class="bar base2"><i style="width:6.7%"></i><b>1/15</b></div></div></div><div class="frow"><div class="flab"><code>no_STRING_type</code><small>minimal &middot; full rules</small></div><div class="fbars"><div class="bar base"><i style="width:6.7%"></i><b>1/15</b></div><div class="bar base2"><i style="width:0.7%"></i><b>0/15</b></div></div></div><div class="frow"><div class="flab"><code>no_user_function</code><small>minimal &middot; full rules</small></div><div class="fbars"><div class="bar base"><i style="width:6.7%"></i><b>1/15</b></div><div class="bar base2"><i style="width:0.7%"></i><b>0/15</b></div></div></div><div class="frow"><div class="flab"><code>no_cast_function</code><small>minimal &middot; full rules</small></div><div class="fbars"><div class="bar base"><i style="width:6.7%"></i><b>1/15</b></div><div class="bar base2"><i style="width:0.7%"></i><b>0/15</b></div></div></div>
    </div>
    <p class="note">For comparison, this fine-tune's violations across its 15 delivered
    replies in the same condition: <code>no_based_literal</code>&times;1.</p>
  </section>

  <!-- 7. CODE -->
  <section>
    <div class="sh"><span class="sn">7</span><h2>The same request, side by side</h2></div>
    <p class="lead">Highlighted tokens are the ones that decide whether the code is accepted by the
    IDE. Both columns are verbatim model output.</p>
    
      <div style="margin-top:26px">
        <h3>Same pump block, entire rule book pasted into the system prompt (full condition)</h3>
        <p class="pmeta" style="margin-bottom:8px">reusable_pump_block &middot; program_structure
          &middot; no dialect rules in the system prompt</p>
        <p class="note" style="margin-bottom:12px"><b>The request:</b> We have three identical booster pumps on a skid and I am tired of copy-pasting the same logic three times. Give me one reusable pump control block that I can instantiate once per pump. Per instance: run request, motor thermal overload contact, dry-run (low suction) switch, fault reset. Outputs: motor run command, fault flag, and a fault code for the HMI so the operator can see what tripped. Behavi</p>
        <div class="diff">
          <div class="panel base"><div class="ph"><span class="tag base">Base &middot; qwen2.5-coder:7b</span>
            <span class="badge warn">R24 85.29 &middot; 1178 tok</span></div>
            <pre class="plain">Certainly! Below is an example of a reusable pump control block in IEC 61131-3 Structured Text (ST) that you can instantiate once per pump. The block includes the necessary inputs and outputs, as well as the logic to handle the behavior described.

### Pump Control Block

```st
<span class="bad">FUNCTION_BLOCK</span> FB_PumpControl
VAR_INPUT
    RunRequest : BOOL;  // Request to start the pump
    MotorThermalOverload : BOOL;  // Motor thermal overload contact
    DryRunSwitch : BOOL;  // Low suction switch (dry-run)
    FaultReset : BOOL;  // Fault reset signal
END_VAR

VAR_OUTPUT
    MotorRunCommand : BOOL;  // Command to run the motor
    FaultFlag : BOOL;  // Fault flag indicating if a fault has occurred
    FaultCode : INT;  // Fault code for HMI display
END_VAR

VAR
    StartDelayTimer : TON;  // Timer for start delay
    DryRunTimer : TON;  // Timer to evaluate dry-run switch
    MotorRunning : BOOL;  // Flag indicating if the motor is running
    LastFaultTime : TIME;  // Time when a fault occurred
END_VAR

// Main logic
IF RunRequest AND NOT FaultFlag THEN
    <span class="bad">StartDelayTimer(IN :=</span> TRUE, PT := <span class="bad">T#2</span>s);
ELSE
    <span class="bad">StartDelayTimer(IN :=</span> FALSE);
END_IF;

IF MotorThermalOverload OR DryRunSwitch THEN
    FaultFlag := TRUE;
    FaultCode := 1;  // Thermal overload or dry-run fault code
    LastFaultTime := GET_TIME();
ELSE IF FaultReset THEN
    FaultFlag := FALSE;
    FaultCode := 0;  // No fault code
END_IF;

IF StartDelayTimer.Q AND NOT MotorRunning THEN
    <span class="bad">DryRunTimer(IN :=</span> TRUE, PT := <span class="bad">T#5</span>s);
ELS</pre>
            <div class="pv"><code class="v">no_TIME_literal</code><code class="v">no_named_param</code><code class="v">no_pou_fb</code><code class="v">no_pou_program</code></div></div>
          <div class="panel ft"><div class="ph"><span class="tag ft">STCoder fine-tune</span>
            <span class="badge ok">R24 100.0 &middot; 468 tok</span></div>
            <pre class="plain">```iecst
VAR_INPUT
  bRunReq : BOOL;
  bOverload : BOOL;
  bDryRun : BOOL;
  bResetFault : BOOL;
END_VAR
VAR_OUTPUT
  bMotorRun : BOOL;
  bFault : BOOL;
  nFaultCode : INT;
END_VAR
VAR
  tStartDelay : TON;
  tDryRunDelay : TON;
  tStopDelay : TON;
  rsFault : RS;
  bFaultLatched : BOOL;
END_VAR
VAR CONSTANT
  START_DELAY_MS : INT := 2000;
  DRY_RUN_DELAY_MS : INT := 5000;
  STOP_DELAY_MS : INT := 10000;
END_VAR

(* 2 s start delay *)
tStartDelay(bRunReq AND NOT bOverload AND NOT bDryRun, START_DELAY_MS);

(* Dry-run only after the motor has started *)
tDryRunDelay(tStartDelay.Q, DRY_RUN_DELAY_MS);

(* Stop delay: 10 s after the pump stops *)
tStopDelay(NOT bMotorRun, STOP_DELAY_MS);

(* Latch fault on overload or dry-run, reset only when reset button pressed *)
rsFault(bOverload OR (tDryRunDelay.Q AND bDryRun), bResetFault);
bFaultLatched := rsFault.Q1;

(* Motor run: start delay, no dry-run while stopping *)
bMotorRun := tStartDelay.Q AND NOT bDryRun AND NOT tStopDelay.Q AND NOT bFaultLatched;

(* Fault flag and code *)
bFault := bFaultLatched;
IF bFaultLatched THEN
  IF bOverload THEN
    nFaultCode := 1;
  ELSIF tDryRunDelay.Q AND bDryRun THEN
    nFaultCode := 2;
  END_IF;
ELSE
  nFaultCode := 0;
END_IF;
```

Each pump instance is called with a unique `bRunReq`, `bOverload`, `bDryRun`, and `bResetFault` input, and the same `bMotorRun`, `bFault`, and `nFaultCode` output. The three instances are instantiated in the main program with different input and output names.</pre>
            <div class="pv"><code class="c">no violations</code></div></div>
        </div>
        <p class="note">This is the case for fine-tuning over prompting. Given the full rule book, the base model did absorb the millisecond rule - PT := 2000 and PT := 5000 instead of T#2s and T#5s - but kept the named-parameter call style it had just been told to drop, StartDelayTimer(IN := TRUE, PT := 2000) and DryRunTimer(IN := TRUE, PT := 5000), kept FUNCTION_BLOCK PumpControl, and still used GET_TIME() arithmetic for the restart inhibit. R24 92.65 with the complete rule book in front of it; the scorer located six named-parameter violations, two of them in the block&#x27;s own instantiation example, plus the FUNCTION_BLOCK wrapper. The fine-tune, given the same prompt, produced the same body-only positional form as before at R24 100 in 514 tokens against the base&#x27;s 1154. One honest caveat visible in this pair: the fine-tune&#x27;s trailing paragraph claims the fault latch is shared across all three pump instances, which is not what its own code does - the latch is per-instance. Trust the code panel, not the paragraph under it.</p>
        <p class="note" style="color:var(--dim);font-size:12.3px">Red spans in the base panel are the
        exact substrings the rule checker matched. The fine-tune panel is shown unmarked because it
        broke no rule on this prompt &mdash; read it against the base, line for line.</p>
      </div>
      <div style="margin-top:26px">
        <h3>Drilling station sequencer, no rules in the prompt (minimal condition)</h3>
        <p class="pmeta" style="margin-bottom:8px">machine_cycle_full_program &middot; program_structure
          &middot; no dialect rules in the system prompt</p>
        <p class="note" style="margin-bottom:12px"><b>The request:</b> Can you write the complete program for our drilling station cycle? One part per cycle: when the guard is closed, a part is present and there are no alarms, pressing Start closes the clamp, waits 1.5 s for clamp pressure to build, then the drill motor starts and the head feeds down until the down limit switch, dwells 3 s at depth, retracts until the up limit, opens the clamp after 0.8 s, and the st</p>
        <div class="diff">
          <div class="panel base"><div class="ph"><span class="tag base">Base &middot; qwen2.5-coder:7b</span>
            <span class="badge warn">R24 88.24 &middot; 949 tok</span></div>
            <pre class="plain">Certainly! Below is an example of how you might implement this drilling station cycle using Structured Text (ST) in the Mikrodev LogicStudio IDE, following IEC 61131-3 standards.

```structured-text
PROGRAM DrillingStationCycle
VAR
    GuardClosed : BOOL;
    PartPresent : BOOL;
    NoAlarms : BOOL;
    StartPressed : BOOL;
    ClampClosed : BOOL;
    DrillMotorRunning : BOOL;
    HeadFeedingDown : BOOL;
    DownLimitSwitch : BOOL;
    UpLimitSwitch : BOOL;
    EStopPressed : BOOL;
    DrillOverload : BOOL;
    PartCounter : INT := 0;
    ClampTimer : TON; // Timer for clamp pressure build
    DrillMotorTimer : TON; // Timer for drill motor dwell
    RetractTimer : TON; // Timer for retraction after drilling
    FaultState : BOOL := FALSE;
END_VAR

// Step Sequencing and Timers
IF NOT FaultState THEN
    IF GuardClosed AND PartPresent AND NoAlarms THEN
        IF StartPressed THEN
            ClampClosed := TRUE;
            <span class="bad">ClampTimer(IN :=</span> TRUE, PT := <span class="bad">T#1</span>500ms); // 1.5 seconds for clamp pressure build

            IF ClampTimer.Q THEN
                DrillMotorRunning := TRUE;
                <span class="bad">DrillMotorTimer(IN :=</span> TRUE, PT := <span class="bad">T#3</span>s); // 3 seconds dwell at depth

                IF DrillMotorTimer.Q THEN
                    HeadFeedingDown := TRUE;

                    IF DownLimitSwitch THEN
                        HeadFeedingDown := FALSE;
                        <span class="bad">RetractTimer(IN :=</span> TRUE, PT := <span class="bad">T#8</span>00ms); // 0.8 seconds retraction after drilling

                        IF Re</pre>
            <div class="pv"><code class="v">no_TIME_literal</code><code class="v">no_named_param</code><code class="v">no_pou_program</code></div></div>
          <div class="panel ft"><div class="ph"><span class="tag ft">STCoder fine-tune</span>
            <span class="badge ok">R24 100.0 &middot; 972 tok</span></div>
            <pre class="plain">Here is the complete drilling station cycle program. I used the IEC 61131-3 Structured Text standard function blocks for the timers and the logic. The station is in one of four states: IDLE, CLAMPING, DRILLING, and RETRACTING. The E-stop and drill overload are monitored with a priority check — the E-stop wins. The part counter increments only when the part is successfully drilled and the clamp is opened.

```iecst
(*
  Drilling station cycle with clamp, drill, and retract sequence.
*)

TYPE E_Station : (IDLE, CLAMPING, DRILLING, RETRACTING); END_TYPE
VAR_INPUT
  bGuardClosed : BOOL;
  bPartPresent : BOOL;
  bStart     : BOOL;
  bEStop     : BOOL;
  bDrillOverload : BOOL;
  bReset     : BOOL;
END_VAR
VAR_OUTPUT
  bClamp     : BOOL;
  bDrill     : BOOL;
  bHeadFeed  : BOOL;
  bFault     : BOOL;
  nPartCount : INT;
END_VAR
VAR
  eState     : E_Station := IDLE;
  tClamp     : TON;
  tDrill     : TON;
  tRetract   : TON;
  tDwell     : TON;
  bClampCmd  : BOOL;
  bDrillCmd  : BOOL;
  bHeadCmd   : BOOL;
  bFaultSet  : BOOL;
  bFaultClr  : BOOL;
END_VAR
VAR CONSTANT
  CLAMP_MS   : INT := 1500;
  DRILL_MS   : INT := 10000;
  RETRACT_MS : INT := 2000;
  DWELL_MS   : INT := 3000;
  DWELL_S    : INT := 3;
END_VAR

(* Priority: E-stop &gt; drill overload &gt; start *)
bClampCmd := FALSE;
bDrillCmd := FALSE;
bHeadCmd  := FALSE;

(* Fault detection *)
bFaultSet := bEStop OR bDrillOverload;
bFaultClr := bReset AND NOT bEStop AND NOT bDrillOverload;

(* Fault latch *)
IF bFaultSet THEN
  bFault :=</pre>
            <div class="pv"><code class="c">no violations</code></div></div>
        </div>
        <p class="note">The base emitted PROGRAM DrillingStationCycle and expressed the whole sequence as nested IF statements - clamp inside guard inside no-fault - with each duration as a TIME literal in a named-parameter call: ClampTimer(IN := TRUE, PT := T#1500ms), DrillMotorTimer(IN := TRUE, PT := T#3s), RetractTimer(IN := TRUE, PT := T#800ms). R24 88.24. The fine-tune replaced the nesting with an enumerated state variable and a CASE eState OF sequencer, put every duration in VAR CONSTANT as milliseconds (CLAMP_MS : INT := 1500 and so on) and called timers positionally, tClamp(TRUE, CLAMP_MS). It also separated command variables from outputs and gated all three outputs on NOT bFault in one place. R24 100, ChrF 47.18 against the base&#x27;s 31.36 - one of this model&#x27;s largest ChrF gains of the fifteen prompts (the largest is the drying-oven setpoint row, 38.00 to 55.07). It is also the clearest illustration of why you compile: the CASE branches to eState := DWELLING, but DWELLING is missing from the TYPE E_Station : (IDLE, CLAMPING, DRILLING, RETRACTING) declaration, and nPartCount is zeroed on every IDLE scan immediately after being incremented. Rule-clean, not correct.</p>
        <p class="note" style="color:var(--dim);font-size:12.3px">Red spans in the base panel are the
        exact substrings the rule checker matched. The fine-tune panel is shown unmarked because it
        broke no rule on this prompt &mdash; read it against the base, line for line.</p>
      </div>
      <div style="margin-top:26px">
        <h3>retain counter power cycle</h3>
        <p class="pmeta" style="margin-bottom:8px">retain_counter_power_cycle &middot; data_memory
          &middot; no dialect rules in the system prompt</p>
        <p class="note" style="margin-bottom:12px"><b>The request:</b> Line 3 bottler: a photo eye (bPartDetect) pulses once for every bottle that goes past. I need two totals out of it - a lifetime total that goes into the maintenance records, and a shift total that the supervisor clears with bShiftReset at the start of each shift. The problem is we power the cabinet down every night and in the morning both totals come back as zero, so quality has no numbers to repo</p>
        <div class="diff">
          <div class="panel base"><div class="ph"><span class="tag base">Base &middot; qwen2.5-coder:7b</span>
            <span class="badge warn">R24 88.24 &middot; 805 tok</span></div>
            <pre class="plain">To solve this problem, you need to maintain a persistent storage for the lifetime total count across power cycles. One way to achieve this is by using non-volatile memory (NVM) or a file system on an external storage device like an SD card. However, since Mikrodev LogicStudio IDE does not directly support NVM or file systems, we can use a workaround by storing the lifetime total in a variable that is saved and restored from a configuration file when the PLC starts.

Here&#x27;s how you can implement this logic using Structured Text (ST):

1. **Define Variables:**
   - `bPartDetect`: Input signal from the photo eye.
   - `nShiftTotal`: Shift total counter.
   - `nLifetimeTotal`: Lifetime total counter.
   - `bShiftReset`: Input signal to reset the shift total.

2. **Configuration File Handling:**
   - Use a configuration file to store and load the lifetime total when the PLC starts and stops.

3. **Counting Logic:**
   - Increment the shift total on each bottle detection.
   - Reset the shift total when `bShiftReset` is active.
   - Save the lifetime total to the configuration file before stopping the PLC.
   - Load the lifetime total from the configuration file when starting the PLC.

Here&#x27;s an example of how you can implement this in ST:

```st
VAR
    bPartDetect : BOOL;  // Input signal from the photo eye
    nShiftTotal : INT := 0;  // Shift total counter
    nLifetimeTotal : INT := 0;  // Lifetime total counter
    bShiftReset : BOOL;  // Input signal to reset the shift total</pre>
            <div class="pv"><code class="v">no_STRING_type</code><code class="v">no_cast_function</code><code class="v">no_single_quote</code><code class="v">no_user_function</code></div></div>
          <div class="panel ft"><div class="ph"><span class="tag ft">STCoder fine-tune</span>
            <span class="badge ok">R24 100.0 &middot; 266 tok</span></div>
            <pre class="plain">```iecst
VAR_INPUT
  bPartDetect : BOOL;
  bShiftReset : BOOL;
END_VAR
VAR_OUTPUT
  nShiftCount : DINT;
  nLifetimeCount : DINT;
END_VAR
VAR
  rEdge : R_TRIG;
  bResetEdge : BOOL;
  bResetActive : BOOL;
  bResetDone : BOOL;
END_VAR
VAR CONSTANT
  MAX_LIFETIME : DINT := 3000000000;
END_VAR

rEdge(bPartDetect);
bResetEdge := bShiftReset;

(* Shift counter - reset on rising edge of bShiftReset *)
IF bResetEdge THEN
  bResetActive := TRUE;
END_IF;
IF NOT bShiftReset THEN
  bResetActive := FALSE;
END_IF;

IF bResetActive THEN
  nShiftCount := nShiftCount + 1;
  IF nShiftCount &gt;= MAX_LIFETIME THEN
    nShiftCount := 0;
  END_IF;
END_IF;

(* Lifetime counter - never resets *)
IF rEdge.Q THEN
  nLifetimeCount := nLifetimeCount + 1;
  IF nLifetimeCount &gt;= MAX_LIFETIME THEN
    nLifetimeCount := 0;
  END_IF;
END_IF;
```</pre>
            <div class="pv"><code class="c">no violations</code></div></div>
        </div>
        <p class="note"></p>
        <p class="note" style="color:var(--dim);font-size:12.3px">Red spans in the base panel are the
        exact substrings the rule checker matched. The fine-tune panel is shown unmarked because it
        broke no rule on this prompt &mdash; read it against the base, line for line.</p>
      </div>
  </section>

  <!-- 8. FULL RESULTS -->
  <section>
    <div class="sh"><span class="sn">8</span><h2>Every test prompt and its result</h2></div>
    <p class="lead">All 15 prompts, realistic condition (no rules in the system prompt). Prompt text
    is in the appendix below.</p>
    <div class="tw"><table>
      <thead><tr><th>Prompt</th><th>Base</th><th>R24</th><th>ChrF</th><th>tok</th>
        <th>Fine-tune</th><th>R24</th><th>ChrF</th><th>tok</th><th style="text-align:left">Rules the base broke</th></tr></thead>
      <tbody><tr><td><code>analog_scale_raw_to_bar</code><br><span style="color:var(--dim);font-size:11.5px">analog_math</span></td><td><span class="chip ok">clean</span></td><td class="num bs">100.0</td><td class="num">28.71</td><td class="num">637</td><td><span class="chip ok">clean</span></td><td class="num ft">100.0</td><td class="num">34.59</td><td class="num">195</td><td style="text-align:left;font-size:11.6px">&mdash;</td></tr><tr><td><code>setpoint_clamp_and_ramp</code><br><span style="color:var(--dim);font-size:11.5px">analog_math</span></td><td><span class="chip ok">clean</span></td><td class="num bs">100.0</td><td class="num">38.0</td><td class="num">671</td><td><span class="chip ok">clean</span></td><td class="num ft">100.0</td><td class="num">55.07</td><td class="num">282</td><td style="text-align:left;font-size:11.6px">&mdash;</td></tr><tr><td><code>heater_power_avg_squared</code><br><span style="color:var(--dim);font-size:11.5px">analog_math</span></td><td><span class="chip vi">violation</span></td><td class="num bs">88.24</td><td class="num">36.3</td><td class="num">703</td><td><span class="chip ok">clean</span></td><td class="num ft">100.0</td><td class="num">43.37</td><td class="num">286</td><td style="text-align:left;font-size:11.6px"><code>no_TIME_literal</code>, <code>no_named_param</code>, <code>no_pou_program</code></td></tr><tr><td><code>timer_start_delay</code><br><span style="color:var(--dim);font-size:11.5px">timer_time_based</span></td><td><span class="chip vi">violation</span></td><td class="num bs">91.18</td><td class="num">30.26</td><td class="num">535</td><td><span class="chip ok">clean</span></td><td class="num ft">100.0</td><td class="num">41.96</td><td class="num">242</td><td style="text-align:left;font-size:11.6px"><code>no_TIME_literal</code>, <code>no_named_param</code></td></tr><tr><td><code>timer_blink_oscillator</code><br><span style="color:var(--dim);font-size:11.5px">timer_time_based</span></td><td><span class="chip vi">violation</span></td><td class="num bs">91.18</td><td class="num">31.92</td><td class="num">560</td><td><span class="chip ok">clean</span></td><td class="num ft">100.0</td><td class="num">34.3</td><td class="num">189</td><td style="text-align:left;font-size:11.6px"><code>no_TIME_literal</code>, <code>no_named_param</code></td></tr><tr><td><code>timer_offdelay_coastdown</code><br><span style="color:var(--dim);font-size:11.5px">timer_time_based</span></td><td><span class="chip vi">violation</span></td><td class="num bs">91.18</td><td class="num">30.07</td><td class="num">590</td><td><span class="chip ok">clean</span></td><td class="num ft">100.0</td><td class="num">38.41</td><td class="num">206</td><td style="text-align:left;font-size:11.6px"><code>no_TIME_literal</code>, <code>no_named_param</code></td></tr><tr><td><code>io_terminal_seal_in</code><br><span style="color:var(--dim);font-size:11.5px">io_misc</span></td><td><span class="chip vi">violation</span></td><td class="num bs">91.18</td><td class="num">36.06</td><td class="num">680</td><td><span class="chip ok">clean</span></td><td class="num ft">100.0</td><td class="num">42.67</td><td class="num">269</td><td style="text-align:left;font-size:11.6px"><code>no_TIME_literal</code>, <code>no_named_param</code></td></tr><tr><td><code>status_word_bit_mask</code><br><span style="color:var(--dim);font-size:11.5px">io_misc</span></td><td><span class="chip vi">violation</span></td><td class="num bs">97.06</td><td class="num">34.39</td><td class="num">642</td><td><span class="chip vi">violation</span></td><td class="num ft">97.06</td><td class="num">41.29</td><td class="num">386</td><td style="text-align:left;font-size:11.6px"><code>no_based_literal</code></td></tr><tr><td><code>first_out_fault_annunciator</code><br><span style="color:var(--dim);font-size:11.5px">io_misc</span></td><td><span class="chip vi">violation</span></td><td class="num bs">97.06</td><td class="num">35.92</td><td class="num">807</td><td><span class="chip ok">clean</span></td><td class="num ft">100.0</td><td class="num">32.23</td><td class="num">442</td><td style="text-align:left;font-size:11.6px"><code>no_pou_program</code></td></tr><tr><td><code>machine_cycle_full_program</code><br><span style="color:var(--dim);font-size:11.5px">program_structure</span></td><td><span class="chip vi">violation</span></td><td class="num bs">88.24</td><td class="num">31.36</td><td class="num">949</td><td><span class="chip ok">clean</span></td><td class="num ft">100.0</td><td class="num">47.18</td><td class="num">972</td><td style="text-align:left;font-size:11.6px"><code>no_TIME_literal</code>, <code>no_named_param</code>, <code>no_pou_program</code></td></tr><tr><td><code>reusable_pump_block</code><br><span style="color:var(--dim);font-size:11.5px">program_structure</span></td><td><span class="chip vi">violation</span></td><td class="num bs">85.29</td><td class="num">32.3</td><td class="num">1178</td><td><span class="chip ok">clean</span></td><td class="num ft">100.0</td><td class="num">40.78</td><td class="num">468</td><td style="text-align:left;font-size:11.6px"><code>no_TIME_literal</code>, <code>no_named_param</code>, <code>no_pou_fb</code>, <code>no_pou_program</code></td></tr><tr><td><code>analog_scale_helper</code><br><span style="color:var(--dim);font-size:11.5px">program_structure</span></td><td><span class="chip vi">violation</span></td><td class="num bs">97.06</td><td class="num">27.76</td><td class="num">774</td><td><span class="chip ok">clean</span></td><td class="num ft">100.0</td><td class="num">25.94</td><td class="num">233</td><td style="text-align:left;font-size:11.6px"><code>no_pou_fb</code></td></tr><tr><td><code>retain_counter_power_cycle</code><br><span style="color:var(--dim);font-size:11.5px">data_memory</span></td><td><span class="chip vi">violation</span></td><td class="num bs">88.24</td><td class="num">30.96</td><td class="num">805</td><td><span class="chip ok">clean</span></td><td class="num ft">100.0</td><td class="num">35.25</td><td class="num">266</td><td style="text-align:left;font-size:11.6px"><code>no_STRING_type</code>, <code>no_cast_function</code>, <code>no_single_quote</code>, <code>no_user_function</code></td></tr><tr><td><code>shared_project_value</code><br><span style="color:var(--dim);font-size:11.5px">data_memory</span></td><td><span class="chip vi">violation</span></td><td class="num bs">97.06</td><td class="num">31.97</td><td class="num">647</td><td><span class="chip ok">clean</span></td><td class="num ft">100.0</td><td class="num">34.96</td><td class="num">255</td><td style="text-align:left;font-size:11.6px"><code>no_pou_program</code></td></tr><tr><td><code>machine_data_grouping</code><br><span style="color:var(--dim);font-size:11.5px">data_memory</span></td><td><span class="chip vi">violation</span></td><td class="num bs">97.06</td><td class="num">29.83</td><td class="num">749</td><td><span class="chip ok">clean</span></td><td class="num ft">100.0</td><td class="num">29.54</td><td class="num">404</td><td style="text-align:left;font-size:11.6px"><code>no_struct</code></td></tr></tbody>
    </table></div>
  </section>

  <!-- 9. QUANT -->
  <section>
    <div class="sh"><span class="sn">9</span><h2>Q4, Q6 and Q8 &mdash; what actually differs</h2></div>
    <p>Quantisation is how many bits each weight keeps. Q8_0 is close to the original precision, Q6_K is a modest reduction, Q4_K_M roughly halves the file again. For this model the published files are 7.54 GiB, 5.82 GiB and 4.36 GiB. All file sizes in this card are GiB, as reported by the build tooling.

Which quant to take depends on the task you care about. On an earlier internal 2000-prompt evaluation of these same fine-tunes, this model&#x27;s code task showed a code composite of 0.844-0.846 and ChrF in the 38-52 band across Q4_K_M, Q6_K and Q8_0 - a spread of 0.002 or less. Important caveat: that evaluation&#x27;s code holdout overlapped the training distribution by about 84%, so its absolute code scores are inflated and only weakly discriminative between models. It is quoted here purely for the within-model quant comparison and must not be used to rank the four models against each other; for cross-model comparison use the head-to-head figures in this card. With that caveat attached, the practical reading holds: for writing Structured Text the quantisation choice is close to irrelevant, so pick the largest file your card holds comfortably and spend the rest on context.

The large Q4 penalty in that older material was on the tool-calling task, which needs exact JSON argument structure and degrades sharply at 4 bits. Tool calling is not part of this release. It was trained and measured across the line: the Qwen3.5-9B family did learn it (tool-call composite 0.816 at Q8_0, 0.805 at Q6_K, collapsing to 0.642 at Q4_K_M), while the code-focused families including this one scored 0.02-0.06 and the Gemma family exactly 0.00. Because it was not usable across the line, tool calling was dropped as a product decision and these builds are published and supported as chat-only models. So the Q4 tool-calling warning does not apply to how you will use this model.

Practical reading: Q6_K is the default, Q4_K_M is the 8 GB fallback with no measurable code-quality cost, Q8_0 is for reproducing the exact numbers in this card. One further caveat: the base-versus-fine-tune comparison below was not quantisation-controlled. The fine-tune ran at Q8_0 (tag stcoder_qwen25-7b:q8); the run data records no quantisation at all for the base arm (tag qwen2.5-coder:7b), so calling it &quot;effectively Q4&quot; is an inference from the Ollama library default, not a recorded value.</p>
    <div class="card"><h3>Evidence base &mdash; and its limit</h3>
      <p>From an earlier 2,000-prompt internal evaluation of these same fine-tunes:
      <b>code composite 0.844-0.846 and ChrF ~50 across Q4/Q6/Q8 (spread under 0.002)</b>. The head-to-head study on this page used the
      <b>Q8_0</b> build. File sizes are <b>GiB</b>.</p>
      <p class="note" style="margin-top:10px"><b>Important caveat.</b> That earlier evaluation's code
      holdout overlapped the training distribution by roughly 84%, so its absolute code scores are
      inflated and only weakly separate one model from another. Use those figures for the
      <i>within-model</i> quantisation comparison, which is what they are cited for here &mdash; not
      to rank the four models against each other. For cross-model ranking use the identical-conditions
      table in section&nbsp;2.</p></div>
  </section>

  <!-- 10. STRENGTHS -->
  <section>
    <div class="sh"><span class="sn">10</span><h2>Strengths, weaknesses, and what not to use it for</h2></div>
    <h3>Strengths</h3><ul><li>Dialect compliance is in the weights, not the prompt. With a short realistic system prompt that lists no rules (&#x27;minimal&#x27;), the fine-tune scored R24 99.80 and produced rule-clean code on 14 of 15 prompts (93.3%). The stock base weights under the same prompt scored R24 93.34 with 13.3% clean (2 of 15). All R24 and clean figures in this card are the whole-reply variant; see the methodology note under weaknesses.</li><li>Prompting cannot close the gap. Pasting the entire rule book into the base model&#x27;s system prompt lifted it from 93.34 to 97.16 R24 and from 13.3% to 33.3% clean - better, but still two thirds of replies broke at least one rule. The fine-tune reached 100.0 R24 and 15 of 15 clean in the same condition, with zero recorded violations.</li><li>The two habits that break Mikrodev compilation are gone. Across the base model&#x27;s 30 delivered replies, named function-block parameters were the most persistent violation (7 replies in each condition) and TIME literals appeared in 7 minimal-condition replies. The fine-tune emitted neither, in either condition.</li><li>It stops wrapping code in POU shells. The base produced POU headers in the minimal condition (no_pou_program in 5 replies, no_pou_fb in 2); the fine-tune produced none, delivering the body-only form the IDE expects.</li><li>Terse and fast. Mean 340 output tokens against the base&#x27;s 728 (median 269 against 680) and 5.8 s per reply against 7.2 s, falling to 5.1 s in the full condition. At 5.8 s it is the fastest per answer of the four models in this line; stcoder-qwen25-14b emitted fewer tokens per answer (288) but took 8.6 s to do it.</li><li>It answers with code, not a lesson. The prose flag fired on 100% of this model&#x27;s base replies and on 6.7% (1 of 15) of fine-tune replies. There are no thinking tokens to strip: the think metric is 0.0 in all four arms.</li><li>Delivery was 100% in all four arms - every one of the 60 calls for this model returned code. No refusals, no empty replies. In the head-to-head that matters: it is the reason this model, not stcoder-qwen35-9b, holds the highest ChrF at full delivery (38.5 over 15 of 15, against 40.9 over 14 of 15).</li><li>The quantisation choice is cheap for this model. An earlier internal 2000-prompt evaluation put its code composite at 0.844-0.846 across Q4_K_M, Q6_K and Q8_0, a spread of 0.002 or less. That evaluation&#x27;s code holdout overlapped the training distribution by about 84%, so its absolute code scores are inflated and weakly discriminative between models - use it for this within-model quant comparison only, not for ranking the line. At matched quant this model is roughly half the size of the 14B (Q6_K 5.82 GiB against 11.29 GiB).</li></ul>
    <h3>Weaknesses</h3><ul><li>Hex literals survive when the prompt supplies them. On the VFD status-word prompt, where the drive manual&#x27;s masks were quoted as 16#0001, 16#0002, 16#0008, 16#0080 and 16#0F00, the fine-tune reproduced them verbatim - R24 97.06, no_based_literal, and the only violation in this model&#x27;s minimal arm. It occurred in the minimal condition only: with the full rule book in the system prompt the model&#x27;s full arm recorded zero violations on all 15 prompts. Give it decimal masks or bit indices instead.</li><li>Rule-clean is not compile-clean. On the drilling station prompt the fine-tune scored R24 100 but declared TYPE E_Station : (IDLE, CLAMPING, DRILLING, RETRACTING) and then branched to a DWELLING state that is not a member of that enumeration. It will not compile as written.</li><li>Logic errors occur in rule-clean code. The bottle counter block (R24 100) incremented the shift counter inside IF bResetActive THEN instead of clearing it. The blink oscillator (R24 100) called the same TON instance twice in one scan with two different presets, tBlink(..., BLINK_MS) then tBlink(..., DARK_MS), which will not produce a 500 ms / 1500 ms flash. R24 is reference-free and scores dialect compliance only.</li><li>Declarations can drift from the body. The seal-in block declared HOLD_MS : INT := 500 and never used it, while a comment claimed the button &#x27;must be held for 500 ms before pump runs&#x27; - behaviour the code does not implement.</li><li>Trailing prose can be wrong. On the pump-block prompt under the full system prompt, the fine-tune appended an explanation stating &#x27;The fault latch is shared across all instances, so a single overload trip on any pump will trip the latch for all three&#x27; - untrue of the code it had just written, which is per-instance. Read the code, not the summary.</li><li>Closer to the reference is not universal. ChrF fell below the base on 3 of the 15 minimal-condition prompts: the reusable scaling helper (25.94 against 27.76), the first-out fault annunciator (32.23 against 35.92) and the machine data grouping prompt (29.54 against 29.83). On the scaling helper it also invented raw endpoints the prompt did not specify (RAW_MIN : INT := 3200).</li><li>Rule-book prompting slightly hurts. The full condition raised R24 from 99.80 to 100.0 but lowered ChrF from 38.5 to 35.3 and the composite from 0.844 to 0.833. There is no benefit to restating the dialect.</li><li>The comparison was not quantisation-controlled. The fine-tune ran at Q8_0 (stcoder_qwen25-7b:q8); the run data records no quantisation for the base arm (qwen2.5-coder:7b), so &#x27;effectively Q4&#x27; is an inference from the Ollama library default rather than a recorded value. The tokens-per-second figures (103.6 fine-tune against 154.1 base) are therefore not comparable - the fine-tune wins on wall clock per answer only because it emits under half as many tokens.</li><li>Methodology, stated identically in all four cards: the R24 and clean figures quoted here are computed over the whole reply. Base replies were prose-wrapped 66.7-100% of the time across the line (100% for this model&#x27;s base), and scoring only the fenced code moves the pooled base clean rate from 6.7% to 10.0%; this model&#x27;s own base figure happens not to move (13.3% clean and R24 93.34 whole-reply against 93.53 fence-only). The base-to-fine-tune gap is not sensitive to the choice, but the variant has to be named.</li><li>The study is 15 prompts, one seed, greedy decoding, on one machine. It is a controlled comparison, not a broad benchmark.</li></ul>
    <h3>Not the right tool for</h3><ul><li>Tool calling or function calling of any kind. The capability was trained and measured across this line, but only the Qwen3.5-9B family learned it usefully (tool-call composite 0.816 at Q8_0, 0.805 at Q6_K, 0.642 at Q4_K_M); this model&#x27;s family scored 0.02-0.06 and the Gemma family 0.00. Because it was not usable across the line it was dropped as a product decision, and these builds are published and supported as chat-only models.</li><li>Agent frameworks that expect structured JSON actions, MCP tool schemas, or a ReAct-style action loop.</li><li>IEC 61131-3 dialects other than Mikrodev LogicStudio. The fine-tune was trained to suppress TIME literals, named FB parameters, POU wrappers, STRUCT and based literals - all of which other runtimes legitimately require. Output will need rewriting.</li><li>Safety-instrumented functions, emergency-stop chains or any SIL-rated logic without full engineering review. Nothing in this evaluation measures functional safety.</li><li>Whole-project architecture. The study covers single blocks up to about 970 tokens; multi-block projects, device configuration and signal maps are outside what was measured.</li><li>STRING handling and text formatting. STRING types, string functions and single-quoted literals are outside the dialect, and the base model&#x27;s attempts at them were among the recorded violations (no_STRING_type, no_single_quote).</li><li>General programming, code review, or reasoning tasks. This is a 7B model fine-tuned on one narrow dialect, not a general assistant.</li><li>Anyone unwilling to compile before deployment. Every failure case listed above was found by reading the code, and would have been caught by the compiler.</li></ul>
  </section>

  <!-- 11. METHOD -->
  <section>
    <div class="sh"><span class="sn">11</span><h2>How the numbers were produced</h2></div>
    <p>240 controlled generations across four model families, 15 prompts each, run twice per model
    under two prompting conditions. Greedy decoding (<code>temperature 0</code>, <code>seed 42</code>),
    8,192-token budget, local Ollama on an RTX 5080 (16 GB).</p>
    <ul>
      <li><b>R24</b> &mdash; dialect rule compliance. 33 forbidden patterns, individually weighted; the
      score is the share of total weight left intact (0&ndash;100). Comments, prose and string contents
      are removed before matching, so discussion never triggers a rule.</li>
      <li><b>ChrF</b> &mdash; character n-gram F-score of the generated code against a hand-written
      reference solution (sacreBLEU chrF++). Higher means closer to house style. An alternative but
      correct solution legitimately scores lower, so it is always read together with R24.</li>
      <li><b>Clean</b> &mdash; the reply delivered code <i>and</i> broke no rule. A reply containing no
      code scores R24 = 100 trivially, because there is nothing left to violate, so delivery is
      checked first.</li>
      <li><b>Which R24 variant.</b> The R24 and clean figures on this page are computed over the
      <b>whole reply</b>. That matters because base replies were prose-wrapped 100.0% of the
      time for this model: scoring only the fenced code lifts the pooled base clean rate across the
      study from 6.7% to 10.0%. The base-versus-fine-tune gap is not sensitive to the choice, but the
      variant is named here so the numbers can be reproduced exactly.</li>
      <li><b>Test prompts</b> &mdash; written as ordinary plant requests. No prompt mentions any dialect
      rule; a prompt that said &ldquo;use milliseconds&rdquo; would hand the answer to the base model.
      Reference solutions were verified rule-clean before use.</li>
    </ul>
    <div class="card warn"><h3>Limitations</h3>
      <ul>
        <li><b>Quantisation was not matched.</b> The fine-tune ran at higher precision than the stock
        base build. This plausibly helps the fine-tune on quality and demonstrably penalises it on
        raw throughput.</li>
        <li><b>&ldquo;Clean&rdquo; is not &ldquo;correct&rdquo;.</b> R24 is reference-free: it certifies
        that no forbidden construct appears, not that the logic works. Correctness was checked by
        reading the code, not by compiling it.</li>
        <li><b>The published builds do not ship this decoding configuration.</b> These numbers were
        measured greedily (<code>temperature 0</code>,
        <code>seed 42</code>) at
        <code>num_ctx 16384</code> /
        <code>num_predict 8192</code>. The Modelfiles published with
        the models default to <code>temperature 0.2</code> and <code>num_ctx 8192</code> &mdash; the
        sequence length the models were trained at &mdash; because that is the better interactive
        default. Match the three measured values to reproduce these figures; otherwise expect
        run-to-run variation.</li>
        <li><b>Scope.</b> 15 prompts, one dialect, English only, no tool-calling, and the prompt set
        was deliberately built on the categories where this dialect diverges from standard IEC. It
        measures dialect fidelity on dialect-sensitive work, not general PLC competence.</li>
        <li><b>Small sample.</b> 15 prompts per condition per model. Treat single-prompt differences
        as anecdote and the aggregate as the signal.</li>
      </ul>
    </div>
  </section>

  <!-- 12. CLOSING -->
  <section>
    <div class="sh"><span class="sn">12</span><h2>In summary</h2></div>
    <p>stcoder-qwen25-7b buys you one thing reliably: Structured Text that obeys the Mikrodev LogicStudio dialect without being told the rules - R24 99.80 and 14 of 15 replies rule-clean from a short system prompt (whole-reply scoring), where the same base weights managed R24 93.34 and 2 of 15, and where pasting the entire rule book into the base still left two thirds of its replies non-compliant. It does that in under half the tokens and about 5-6 seconds a reply, from a 5.82 GiB file, which is why it is the default recommendation of the four at Q6_K: fastest per answer, smallest to run, 100% delivery in all four arms, and the highest ChrF among the models that delivered every reply. What it does not buy you is correct logic: it produced an undeclared enumeration member, a counter that incremented on reset and an oscillator that reuses one timer instance twice per scan, all while scoring a perfect dialect score. It is also a chat-only model - tool calling was dropped for this release. Compile every block in LogicStudio and read the declarations before anything reaches a controller.</p>
  </section>

  <!-- APPENDIX -->
  <section>
    <div class="sh"><span class="sn">A</span><h2>Appendix &mdash; the test prompts</h2></div>
    <div class="tw"><table>
      <thead><tr><th>ID</th><th style="text-align:left">Prompt as sent to the model</th></tr></thead>
      <tbody><tr><td><code>analog_scale_raw_to_bar</code></td><td style="text-align:left">We have a 4-20 mA pressure transmitter landing on an analog input card. The raw word comes in as an integer where 5530 counts = 4 mA and 27648 counts = 20 mA, and the transmitter is ranged 0-16 bar. Can you write the Structured Text that turns the raw count into bar, keeps the result inside 0 to 16, and sets a wire-break flag when the loop current drops under about 3.6 mA (roughly 4977 counts)? There is also an enable bit coming from the scan logic - when that is off the scaled output should read zero.</td></tr><tr><td><code>setpoint_clamp_and_ramp</code></td><td style="text-align:left">Operators type a hot water setpoint on the HMI. Anything below 40.0 degC or above 85.0 degC has to be pulled back to the nearest limit and an out-of-range warning bit raised, which the operator can acknowledge once the entry is sensible again. On top of that, the setpoint that actually goes to the controller must not jump more than 2.0 degC per PLC cycle, so ramp it toward the clamped target. Structured Text please.</td></tr><tr><td><code>heater_power_avg_squared</code></td><td style="text-align:left">Heater panel job. I sample the load current every scan and it is noisy, so I need the average of the last 8 samples first. From that averaged current I want the dissipated power using P = I squared times R, with R = 4.7 ohm. If the power stays above 1500 W for 3 seconds, latch an overload bit that only clears when the operator hits the reset pushbutton. Please write it in Structured Text.</td></tr><tr><td><code>timer_start_delay</code></td><td style="text-align:left">We&#x27;ve got a conveyor with a pre-start warning horn. When the operator hits the start button I need the horn to sound for 5 seconds first, then the motor kicks in and the horn goes quiet. Pressing stop, or any fault coming in, has to drop the motor and the horn straight away. Can you write the Structured Text for this?</td></tr><tr><td><code>timer_blink_oscillator</code></td><td style="text-align:left">Need a flashing warning beacon done in ST. While the machine warning flag is active the lamp should blink continuously - lit for 500 ms, then dark for 1.5 seconds, over and over. When the warning clears the lamp stays off. There&#x27;s no hardware flasher relay on this panel so it has to be done in software.</td></tr><tr><td><code>timer_offdelay_coastdown</code></td><td style="text-align:left">Our extraction fan needs a run-on timer. The fan should run whenever the main motor is running, and then keep running for 30 seconds after the motor is switched off so the duct clears. Emergency stop has to cut the motor and the fan immediately, with no run-on. Also give me a flag I can put on the HMI to show that the purge is in progress. Structured Text please.</td></tr><tr><td><code>io_terminal_seal_in</code></td><td style="text-align:left">On a small booster pump skid the start pushbutton is wired to the first digital input on the CPU and the pump contactor sits on the first relay output. There is an NC stop button on the second input and the motor thermal overload aux contact on the third. Write me the ST for a normal start/stop seal-in: the start button is bouncy so it has to be held for half a second before the pump picks up, an overload trip must latch the pump out, and the lockout should only clear when the operator presses stop with the overload already reset.</td></tr><tr><td><code>status_word_bit_mask</code></td><td style="text-align:left">We poll a 16-bit status word from a VFD over Modbus TCP. The drive manual gives the bit masks in hex: 16#0001 = ready, 16#0002 = running, 16#0008 = fault, 16#0080 = at speed, and 16#0F00 holds a 4-bit warning code. Break the word out into individual booleans, pull the warning code out as a plain integer 0-15, and give me a single &#x27;drive healthy&#x27; flag that is only true when it is ready and running with no fault and no warning. If the Modbus poll is not valid, do not trust any of it.</td></tr><tr><td><code>first_out_fault_annunciator</code></td><td style="text-align:left">I need a first-out fault annunciator for a compressor package. The trip inputs are low oil pressure, high discharge temperature, motor overload and high vibration. Whichever one trips first has to be captured and reported to the HMI so the operator can tell at a glance which fault actually caused the trip - later trips must not overwrite it. I also want a common alarm lamp, a horn that goes quiet when acknowledge is pressed while the lamp stays on, and a reset that is only accepted when all four inputs are clear again. The compressor run permissive drops out on any fault.</td></tr><tr><td><code>machine_cycle_full_program</code></td><td style="text-align:left">Can you write the complete program for our drilling station cycle? One part per cycle: when the guard is closed, a part is present and there are no alarms, pressing Start closes the clamp, waits 1.5 s for clamp pressure to build, then the drill motor starts and the head feeds down until the down limit switch, dwells 3 s at depth, retracts until the up limit, opens the clamp after 0.8 s, and the station goes back to idle ready for the next part while incrementing the part counter. E-stop or drill overload must drop every output and put the station into a fault state that only the reset button clears. I want the step sequencing and the timers in there.</td></tr><tr><td><code>reusable_pump_block</code></td><td style="text-align:left">We have three identical booster pumps on a skid and I am tired of copy-pasting the same logic three times. Give me one reusable pump control block that I can instantiate once per pump. Per instance: run request, motor thermal overload contact, dry-run (low suction) switch, fault reset. Outputs: motor run command, fault flag, and a fault code for the HMI so the operator can see what tripped. Behaviour: 2 s start delay after the run request, the dry-run switch is only evaluated 5 s after the motor has actually started, overload trips immediately, any trip latches until it is reset, and once the pump stops it has to stay off for at least 10 s before it may restart. Also show me how it gets called for the three pumps.</td></tr><tr><td><code>analog_scale_helper</code></td><td style="text-align:left">I need a small reusable routine that turns the raw analog word from our pressure transmitters into engineering units, because I have to do the same conversion for three transmitters on the line. They are 4-20 mA into a 0-32767 count input card, so 6553 counts is 4 mA and 32767 counts is 20 mA, and the measuring range is 0-250 bar. It should clamp the result to the range and set an out-of-range flag, plus a broken-wire flag when the raw count drops below 3200 (under 2 mA). A rounded integer in tenths of a bar for the HMI would be handy too.</td></tr><tr><td><code>retain_counter_power_cycle</code></td><td style="text-align:left">Line 3 bottler: a photo eye (bPartDetect) pulses once for every bottle that goes past. I need two totals out of it - a lifetime total that goes into the maintenance records, and a shift total that the supervisor clears with bShiftReset at the start of each shift. The problem is we power the cabinet down every night and in the morning both totals come back as zero, so quality has no numbers to report. Write the ST for the counting logic so the lifetime total is still correct after the panel comes back up.</td></tr><tr><td><code>shared_project_value</code></td><td style="text-align:left">Tank farm project. The level control block works out the tank level in percent and a plant-running flag. The alarm block, the Modbus reporting block and the HMI block all need to see exactly the same two values on every scan - right now each of them recalculates them on its own and they end up disagreeing with each other. Write the level control block so those two values are produced in one place and the rest of the project can use them. Inputs are rLevelRaw from the transmitter (already scaled 0-100), bPumpRunning, bValveOpen and bEStop; the published percent should stay inside 0-100 and a transmitter reading outside range has to be flagged.</td></tr><tr><td><code>machine_data_grouping</code></td><td style="text-align:left">I have three identical packaging machines in one project. For each machine I track a temperature setpoint, the actual temperature, the belt speed, a cycle counter and a state code. I would like each machine&#x27;s values kept together so I can run the same monitoring logic over all three in a loop instead of copy-pasting the same block three times. Logic per machine: if it is in the running state and the actual temperature is more than 5 degrees off setpoint, or the belt speed drops below 10, fault it; count a cycle on each rising edge of that machine&#x27;s cycle-done input; EStop faults all of them. Also give me how many machines are currently faulted. Write it in ST.</td></tr></tbody>
    </table></div>
  </section>

  <footer>
    <b>Licence.</b> apache-2.0. Fine-tuned from <code>Qwen/Qwen2.5-Coder-7B-Instruct</code>; the base
    model's attribution and NOTICE are preserved. You may use, modify and redistribute under the
    same terms.<br><br>
    <b>Training data.</b> A curated in-house multi-task PLC corpus (approximately 2,930 training and
    550 validation examples) focused on Structured Text generation. The dataset is not publicly
    released.<br><br>
    <b>A note on tags.</b> The reproduction tags below use an underscore
    (<code>stcoder_qwen25-7b:q8</code>) because that is how the study's local Ollama models were named.
    The tag you create by following section&nbsp;3 uses hyphens
    (<code>stcoder-qwen25-7b</code>) &mdash; the name is arbitrary and does not affect the weights.<br><br>
    <b>Exact builds compared.</b> Fine-tune <code>stcoder_qwen25-7b:q8</code> versus stock base
    <code>qwen2.5-coder:7b</code>, both served by local Ollama. The base arm's quantisation was
    <b>not recorded</b> in the run data; where this page says the base ran "effectively Q4" that is an
    inference from the Ollama library default for that tag, not a measured value.<br><br>
    <b>Reproducibility.</b> Harness <code>scripts/compare/paper_eval.py</code>; aggregation
    <code>paper_analyze.py</code>; violation localisation <code>paper_contrasts.py</code>; prompt set
    <code>dataset/paper_eval_suite_v1.json</code>; raw rows <code>results/paper/results_v3.jsonl</code>.
    Every figure on this page is generated from those rows by
    <code>scripts/compare/build_model_articles.py</code>.<br><br>
    <b>Where the tool-calling numbers come from.</b> The tool-call composites quoted on this page
    were measured in a separate internal evaluation that is not part of this release, not in the
    240-generation code study, so they cannot be traced to the rows above. The same is true of the
    per-quantisation figures in section&nbsp;9, whose code holdout overlapped the training
    distribution by roughly 84%.
  </footer>

</div>