forked from ckolivas/cgminer
/
driver-gekko.c
4670 lines (4065 loc) · 133 KB
/
driver-gekko.c
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
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Copyright 2017-2021 vh
* Copyright 2021-2022 sidehack
* Copyright 2021-2023 kano
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at your option)
* any later version. See COPYING for more details.
*/
#include "driver-gekko.h"
#include "crc.h"
#include "compat.h"
#include <unistd.h>
#ifdef __GNUC__
#if __GNUC__ >= 7
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#endif
#endif
// usleep reliability
#if defined(__APPLE__)
#define USLEEPMIN 2000
#define USLEEPPLUS 200
#elif defined (WIN32)
#define USLEEPMIN 250
#define USLEEPPLUS 100
#else
#define USLEEPMIN 200
#define USLEEPPLUS 50
#endif
static bool compac_prepare(struct thr_info *thr);
static pthread_mutex_t static_lock = PTHREAD_MUTEX_INITIALIZER;
static bool last_widescreen;
static uint8_t dev_init_count[0xffff] = {0};
static uint8_t *init_count;
static uint32_t stat_len;
static uint32_t chip_max;
#define MS2US(_n) ((_n) * 1000)
// report averages and how far they overrun the requested time
// linux would appear to be unable to handle less than 55us
// on the RPi4 it would regularly sleep 3 times as long
// thus code in general ignores sleeping anything less than 200us
#define TUNE_CODE 1
static void gekko_usleep(struct COMPAC_INFO *info, int usec)
{
#if TUNE_CODE
struct timeval stt, fin;
double td, fac;
#endif
// error for usleep()
if (usec >= 1000000)
{
cgsleep_ms(usec / 1000);
#if TUNE_CODE
mutex_lock(&info->slock);
info->inv++;
mutex_unlock(&info->slock);
#endif
return;
}
#if TUNE_CODE
cgtime(&stt);
#endif
usleep(usec);
#if TUNE_CODE
cgtime(&fin);
td = us_tdiff(&fin, &stt);
fac = (td / (double)usec);
mutex_lock(&info->slock);
if (td < usec)
info->num0++;
if (fac >= 1.5)
{
info->req1_5 += usec;
info->fac1_5 += fac;
info->num1_5++;
}
else
{
if (fac >= 1.1)
{
info->req1_1 += usec;
info->fac1_1 += fac;
info->num1_1++;
}
else
{
info->req += usec;
info->fac += fac;
info->num++;
}
}
mutex_unlock(&info->slock);
#endif
}
static float fbound(float value, float lower_bound, float upper_bound)
{
if (value < lower_bound)
return lower_bound;
if (value > upper_bound)
return upper_bound;
return value;
}
uint32_t bmcrc(unsigned char *ptr, uint32_t len)
{
unsigned char c[5] = {1, 1, 1, 1, 1};
uint32_t i, c1, ptr_idx = 0;
for (i = 0; i < len; i++) {
c1 = c[1];
c[1] = c[0];
c[0] = c[4] ^ ((ptr[ptr_idx] & (0x80 >> (i % 8))) ? 1 : 0);
c[4] = c[3];
c[3] = c[2];
c[2] = c1 ^ c[0];
if (((i + 1) % 8) == 0)
ptr_idx++;
}
return (c[4] * 0x10) | (c[3] * 0x08) | (c[2] * 0x04) | (c[1] * 0x02) | (c[0] * 0x01);
}
void dumpbuffer(struct cgpu_info *compac, int LOG_LEVEL, char *note, unsigned char *ptr, uint32_t len)
{
if (opt_log_output || LOG_LEVEL <= opt_log_level) {
char str[2048];
const char * hex = "0123456789ABCDEF";
char * pout = str;
unsigned int i = 0;
for(; i < 768 && i < len - 1; ++i) {
*pout++ = hex[(*ptr>>4)&0xF];
*pout++ = hex[(*ptr++)&0xF];
if (i % 42 == 41) {
*pout = 0;
pout = str;
applog(LOG_LEVEL, "%i: %s %s: %s", compac->cgminer_id, compac->drv->name, note, str);
} else {
*pout++ = ':';
}
}
*pout++ = hex[(*ptr>>4)&0xF];
*pout++ = hex[(*ptr)&0xF];
*pout = 0;
applog(LOG_LEVEL, "%d: %s %d - %s: %s", compac->cgminer_id, compac->drv->name, compac->device_id, note, str);
}
}
static int compac_micro_send(struct cgpu_info *compac, uint8_t cmd, uint8_t channel, uint8_t value)
{
struct COMPAC_INFO *info = compac->device_data;
int bytes = 1;
int read_bytes = 1;
int micro_temp;
uint8_t temp;
unsigned short usb_val;
__maybe_unused char null[255];
// synchronous : safe to run in the listen thread.
if (!info->micro_found) {
return 0;
}
// Baud Rate : 500,000
usb_val = (FTDI_BITMODE_CBUS << 8) | 0xF3; // low byte: bitmask - 1111 0011 - CB1(HI), CB0(HI)
usb_transfer(compac, FTDI_TYPE_OUT, FTDI_REQUEST_BITMODE, usb_val, info->interface, C_SETMODEM);
gekko_usleep(info, MS2US(2));
//usb_transfer(compac, FTDI_TYPE_OUT, FTDI_REQUEST_BAUD, 0x06, (FTDI_INDEX_BAUD_BTS & 0xff00) | info->interface, C_SETBAUD);
info->cmd[0] = cmd | channel;
info->cmd[1] = value;
if (value != 0x00 || cmd == M2_SET_VCORE) {
bytes = 2;
}
usb_read_timeout(compac, (char *)info->rx, 255, &read_bytes, 1, C_GETRESULTS);
dumpbuffer(compac, LOG_INFO, "(micro) TX", info->cmd, bytes);
usb_write(compac, (char *)info->cmd, bytes, &read_bytes, C_REQUESTRESULTS);
memset(info->rx, 0, info->rx_len);
usb_read_timeout(compac, (char *)info->rx, 1, &read_bytes, 5, C_GETRESULTS);
if (read_bytes > 0) {
dumpbuffer(compac, LOG_INFO, "(micro) RX", info->rx, read_bytes);
switch (cmd) {
case 0x20:
temp = info->rx[0];
micro_temp = 32 + 1.8 * temp;
if (micro_temp != info->micro_temp) {
info->micro_temp = micro_temp;
applog(LOG_WARNING, "%d: %s %d - micro temp changed to %d°C / %.1f°F",
compac->cgminer_id, compac->drv->name, compac->device_id, temp, info->micro_temp);
}
break;
default:
break;
}
}
// Restore Baud Rate
//usb_transfer(compac, FTDI_TYPE_OUT, FTDI_REQUEST_BAUD, (info->bauddiv + 1), (FTDI_INDEX_BAUD_BTS & 0xff00) | info->interface, C_SETBAUD);
usb_val = (FTDI_BITMODE_CBUS << 8) | 0xF2; // low byte: bitmask - 1111 0010 - CB1(HI), CB0(LO)
usb_transfer(compac, FTDI_TYPE_OUT, FTDI_REQUEST_BITMODE, usb_val, info->interface, C_SETMODEM);
gekko_usleep(info, MS2US(2));
return read_bytes;
}
#define compac_send(_c, _r, _b, _crc) compac_send2(_c, _r, _b, _crc, NULL)
static void compac_send2(struct cgpu_info *compac, unsigned char *req_tx, uint32_t bytes, uint32_t crc_bits, __maybe_unused char *msg)
{
struct COMPAC_INFO *info = compac->device_data;
int read_bytes = 1;
unsigned int i, off = 0;
// leave original buffer intact
if (info->asic_type == BM1397)
{
info->cmd[0] = 0x55;
info->cmd[1] = 0xAA;
off = 2;
}
for (i = 0; i < bytes; i++)
info->cmd[i+off] = req_tx[i];
bytes += off;
info->cmd[bytes-1] |= bmcrc(req_tx, crc_bits);
#if 0
if (msg == NULL)
msg = "null";
applog(LOG_ERR, "%s() %d: %s %d - Send len %3u (%s)", __func__,
compac->cgminer_id, compac->drv->name, compac->device_id, bytes, msg);
applog(LOG_ERR, "%s() [%02x %02x %02x %02x %02x %02x %02x %02x]", __func__,
info->cmd[0], info->cmd[1], info->cmd[2], info->cmd[3], info->cmd[4], info->cmd[5], info->cmd[6], info->cmd[7]);
applog(LOG_ERR, "%s() [%02x %02x %02x %02x %02x %02x %02x %02x]", __func__,
info->cmd[8], info->cmd[9], info->cmd[10], info->cmd[11], info->cmd[12], info->cmd[13], info->cmd[14], info->cmd[15]);
#endif
int log_level = (bytes < info->task_len) ? LOG_INFO : LOG_INFO;
dumpbuffer(compac, log_level, "TX", info->cmd, bytes);
usb_write(compac, (char *)(info->cmd), bytes, &read_bytes, C_REQUESTRESULTS);
//let the usb frame propagate
if (info->asic_type == BM1397)
gekko_usleep(info, info->usb_prop);
else
gekko_usleep(info, MS2US(1));
}
static float limit_freq(struct COMPAC_INFO *info, float freq, bool zero)
{
switch(info->ident)
{
case IDENT_BSC:
case IDENT_GSC:
case IDENT_BSD:
case IDENT_GSD:
case IDENT_BSE:
case IDENT_GSE:
freq = fbound(freq, info->freq_base, 500);
break;
case IDENT_GSH:
case IDENT_GSI:
freq = fbound(freq, 50, 900);
break;
case IDENT_GSF:
case IDENT_GSFM:
// allow 0 also if zero is true - coded obviously
if (zero && freq == 0)
freq = 0;
else
freq = fbound(freq, 100, 800);
break;
default:
// 'should' never happen ...
freq = fbound(freq, 100, 300);
break;
}
return freq;
}
static void ping_freq(struct cgpu_info *compac, int asic)
{
struct COMPAC_INFO *info = compac->device_data;
bool ping = false;
if (info->asic_type == BM1397)
{
unsigned char pingall[] = {0x52, 0x05, 0x00, BM1397FREQ, 0x00};
compac_send2(compac, pingall, sizeof(pingall), 8 * sizeof(pingall) - 8, "pingfreq");
ping = true;
}
else if (info->asic_type == BM1387)
{
unsigned char buffer[] = {0x44, 0x05, 0x00, 0x0C, 0x00}; // PLL_PARAMETER
buffer[2] = (0x100 / info->chips) * asic;
compac_send(compac, buffer, sizeof(buffer), 8 * sizeof(buffer) - 8);
ping = true;
}
else if (info->asic_type == BM1384)
{
unsigned char buffer[] = {0x04, 0x00, 0x04, 0x00};
buffer[1] = (0x100 / info->chips) * asic;
compac_send(compac, buffer, sizeof(buffer), 8 * sizeof(buffer) - 5);
ping = true;
}
if (ping)
{
cgtime(&info->last_frequency_ping);
cgtime(&(info->asics[asic].last_frequency_ping));
}
}
static void gsf_calc_nb2c(struct cgpu_info *compac)
{
struct COMPAC_INFO *info = compac->device_data;
int c, i, j;
double fac;
if (info->chips == 1)
{
// default all 0 is correct
info->nb2c_setup = true;
}
else if (info->chips == 6)
{
// groups of 4
fac = CHIPPY1397(info, 1) / 4.0;
for (i = 0; i < 256; i += 64)
{
for (j = 0; j < 64; j++)
{
c = (int)((double)j / fac);
if (c >= (int)(info->chips))
c = info->chips - 1;
info->nb2chip[i + j] = c;
}
}
info->nb2c_setup = true;
}
}
static void gc_wipe(struct GEKKOCHIP *gc, struct timeval *now)
{
// clear out everything
gc->zerosec = now->tv_sec;
gc->offset = 0;
memset(gc->noncenum, 0, sizeof(gc->noncenum));
gc->noncesum = 0;
gc->last = 0;
}
static void gc_wipe_all(struct COMPAC_INFO *info, struct timeval *now, bool locked)
{
int i;
if (!locked)
mutex_lock(&info->ghlock);
for (i = 0; i < (int)(info->chips); i++)
gc_wipe(&(info->asics[i].gc), now);
if (!locked)
mutex_unlock(&info->ghlock);
}
// update asic->gc offset as at 'now' and correct values
// info must be locked, wipe creates a new data set
static void gc_offset(struct COMPAC_INFO *info, struct ASIC_INFO *asic, struct timeval *now, bool wipe, bool locked)
{
struct GEKKOCHIP *gc = &(asic->gc);
time_t delta;
if (!locked)
mutex_lock(&info->ghlock);
// wipe or delta != 0
if (wipe || !CHCMP(gc->zerosec, now->tv_sec))
{
// clear some/all delta data
delta = CHBASE(now->tv_sec) - CHBASE(gc->zerosec);
// if time goes back, also reset everything
// a forward jump of CHNUM will reset the whole buffer
if (wipe || delta < 0 || delta >= CHNUM)
gc_wipe(gc, now);
else
{
// delta is > 0, but usually always 1 unless,
// due to asic failure, a 10 minutes had no nonces
// however the loop will total 1 iteration each
// 10 minutes elapsed real time e.g. if not called
// for 30 minutes, it will loop 3 times
// there is also a CHNUM-1 limit on that
gc->zerosec = now->tv_sec;
// clear out the old values
do
{
gc->offset = CHOFF(gc->offset+1);
gc->noncesum -= gc->noncenum[CHOFF(gc->offset)];
gc->noncenum[CHOFF(gc->offset)] = 0;
if (gc->last < (CHNUM-1))
gc->last++;
}
while (--delta > 0);
}
}
// if there's been no nonces up to now, history must already be all zero
// so just remove history
if (gc->noncesum == 0 && gc->last > 0)
gc->last = 0;
if (!locked)
mutex_unlock(&info->ghlock);
}
// update info->gh offset as at 'now' and correct values
// info must be locked, wipe creates a new data set and also wipes all asic->gc
static void gh_offset(struct COMPAC_INFO *info, struct timeval *now, bool wipe, bool locked)
{
struct GEKKOHASH *gh = &(info->gh);
time_t delta;
int i;
if (!locked)
mutex_lock(&info->ghlock);
// first time in, wipe is ignored (it's already all zero)
if (gh->zerosec == 0)
{
gh->zerosec = now->tv_sec;
for (i = 0; i < (int)(info->chips); i++)
info->asics[i].gc.zerosec = now->tv_sec;
}
else
{
if (wipe)
gc_wipe_all(info, now, true);
// wipe or delta != 0
if (wipe || gh->zerosec != now->tv_sec)
{
// clear some/all delta data
delta = now->tv_sec - gh->zerosec;
// if time goes back, also reset everything
// N.B. a forward time jump between 2 and GHLIMsec
// seconds will reduce the hash rate value
// but GHLIMsec or more will reset the whole buffer
if (wipe || delta < 0 || delta >= GHLIMsec)
{
// clear out everything
gh->zerosec = now->tv_sec;
gh->offset = 0;
memset(gh->diff, 0, sizeof(gh->diff));
memset(gh->firstt, 0, sizeof(gh->firstt));
memset(gh->firstd, 0, sizeof(gh->firstd));
memset(gh->lastt, 0, sizeof(gh->lastt));
memset(gh->noncenum, 0, sizeof(gh->noncenum));
gh->diffsum = 0;
gh->noncesum = 0;
gh->last = 0;
}
else
{
// delta is > 0, but usually always 1 unless,
// due to asic failure, a second had no nonces
// however the loop will total 1 iteration each
// second elapsed real time e.g. if not called
// for 3 seconds, it will loop 3 times
// there is also a GHLIMsec-1 limit on that
gh->zerosec = now->tv_sec;
// clear out the old values
do
{
gh->offset = GHOFF(gh->offset+1);
gh->diffsum -= gh->diff[GHOFF(gh->offset)];
gh->diff[GHOFF(gh->offset)] = 0;
gh->noncesum -= gh->noncenum[GHOFF(gh->offset)];
gh->noncenum[GHOFF(gh->offset)] = 0;
gh->firstt[GHOFF(gh->offset)].tv_sec = 0;
gh->firstt[GHOFF(gh->offset)].tv_usec = 0;
gh->firstd[GHOFF(gh->offset)] = 0;
gh->lastt[GHOFF(gh->offset)].tv_sec = 0;
gh->lastt[GHOFF(gh->offset)].tv_usec = 0;
if (gh->last < (GHNUM-1))
gh->last++;
}
while (--delta > 0);
}
}
}
// if there's been no nonces up to now, history must already be all zero
// so just remove history
if (gh->noncesum == 0 && gh->last > 0)
gh->last = 0;
// this also handles the issue of a nonce-less wipe with a high
// now->tv_usec and if the first nonce comes in during the next second.
// without setting 'last=0' the previous empty full second(s) will
// always be included in the elapsed time used to calc the hash rate
if (!locked)
mutex_unlock(&info->ghlock);
}
// update info->gh with a new nonce as at 'now' (diff=info->difficulty)
// info must be locked, wipe creates a new data set with the single nonce
static void add_gekko_nonce(struct COMPAC_INFO *info, struct ASIC_INFO *asic, struct timeval *now)
{
struct GEKKOHASH *gh = &(info->gh);
mutex_lock(&info->ghlock);
gh_offset(info, now, false, true);
if (gh->diff[gh->offset] == 0)
{
gh->firstt[gh->offset].tv_sec = now->tv_sec;
gh->firstt[gh->offset].tv_usec = now->tv_usec;
gh->firstd[gh->offset] = info->difficulty;
}
gh->lastt[gh->offset].tv_sec = now->tv_sec;
gh->lastt[gh->offset].tv_usec = now->tv_usec;
gh->diff[gh->offset] += info->difficulty;
gh->diffsum += info->difficulty;
(gh->noncenum[gh->offset])++;
(gh->noncesum)++;
if (asic != NULL)
{
struct GEKKOCHIP *gc = &(asic->gc);
gc_offset(info, asic, now, false, true);
(gc->noncenum[gc->offset])++;
(gc->noncesum)++;
}
mutex_unlock(&info->ghlock);
}
// calculate MH/s hashrate, info must be locked
// value is 0.0 if there's no useful data
// caller check info->gh.last for history size used and info->gh.noncesum-1
// for the amount of data used (i.e. accuracy of the hash rate)
static double gekko_gh_hashrate(struct COMPAC_INFO *info, struct timeval *now, bool locked)
{
struct GEKKOHASH *gh = &(info->gh);
struct timeval age, end;
int zero, last;
uint64_t delta;
double ghr, old;
ghr = 0.0;
if (!locked)
mutex_lock(&info->ghlock);
// can't be calculated with only one nonce
if (gh->diffsum > 0 && gh->noncesum > 1)
{
gh_offset(info, now, false, true);
if (gh->diffsum > 0 && gh->noncesum > 1)
{
// offset of 'now'
zero = gh->offset;
// offset of oldest nonce
last = GHOFF(zero - gh->last);
if (gh->diff[last] != 0)
{
// from the oldest nonce, excluding it's diff
delta = gh->firstd[last];
age.tv_sec = gh->firstt[last].tv_sec;
age.tv_usec = gh->firstt[last].tv_usec;
}
else
{
// if last is empty, use the start time of last
delta = 0;
age.tv_sec = gh->zerosec - (GHNUM - 1);
age.tv_usec = 0;
}
// up to the time of the newest nonce as long as it
// was curr or prev second, otherwise use now
if (gh->diff[zero] != 0)
{
// time of the newest nonce found this second
end.tv_sec = gh->lastt[zero].tv_sec;
end.tv_usec = gh->lastt[zero].tv_usec;
}
else
{
// unexpected ... no recent nonces ...
if (gh->diff[GHOFF(zero-1)] == 0)
{
end.tv_sec = now->tv_sec;
end.tv_usec = now->tv_usec;
}
else
{
// time of the newest nonce found this second-1
end.tv_sec = gh->lastt[GHOFF(zero-1)].tv_sec;
end.tv_usec = gh->lastt[GHOFF(zero-1)].tv_usec;
}
}
old = tdiff(&end, &age);
if (old > 0.0)
{
ghr = (double)(gh->diffsum - delta)
* (pow(2.0, 32.0) / old) / 1.0e6;
}
}
}
if (!locked)
mutex_unlock(&info->ghlock);
return ghr;
}
static void job_offset(struct COMPAC_INFO *info, struct timeval *now, bool wipe, bool locked)
{
struct GEKKOJOB *job = &(info->job);
time_t delta;
int jobnow;
jobnow = JOBTIME(now->tv_sec);
if (!locked)
mutex_lock(&info->joblock);
// first time in, wipe is ignored (it's already all zero)
if (job->zeromin == 0)
job->zeromin = jobnow;
else
{
// wipe or delta != 0
if (wipe || job->zeromin != jobnow)
{
// clear some/all delta data
delta = jobnow - job->zeromin;
// if time goes back, also reset everything
// N.B. a forward time jump between 2 and JOBLIMn
// seconds will reduce the job rate value
// but JOBLIMn or more will reset the whole buffer
if (wipe || delta < 0 || delta >= JOBLIMn)
{
// clear out everything
job->zeromin = jobnow;
job->lastjob.tv_sec = 0;
job->lastjob.tv_usec = 0;
job->offset = 0;
memset(job->firstj, 0, sizeof(job->firstj));
memset(job->lastj, 0, sizeof(job->lastj));
memset(job->jobnum, 0, sizeof(job->jobnum));
memset(job->avgms, 0, sizeof(job->avgms));
memset(job->minms, 0, sizeof(job->minms));
memset(job->maxms, 0, sizeof(job->maxms));
job->jobsnum = 0;
job->last = 0;
}
else
{
// delta is > 0, but usually always 1 unless,
// due to asic or pool failure, a minute had no jobs
// however the loop will total 1 iteration each
// minute elapsed real time e.g. if not called
// for 2 minutes, it will loop 2 times
// there is also a JOBLIMn-1 limit on that
job->zeromin = jobnow;
// clear out the old values
do
{
job->offset = JOBOFF(job->offset+1);
job->firstj[JOBOFF(job->offset)].tv_sec = 0;
job->firstj[JOBOFF(job->offset)].tv_usec = 0;
job->lastj[JOBOFF(job->offset)].tv_sec = 0;
job->lastj[JOBOFF(job->offset)].tv_usec = 0;
job->jobsnum -= job->jobnum[JOBOFF(job->offset)];
job->jobnum[JOBOFF(job->offset)] = 0;
job->avgms[JOBOFF(job->offset)] = 0;
job->minms[JOBOFF(job->offset)] = 0;
job->maxms[JOBOFF(job->offset)] = 0;
if (job->last < (JOBMIN-1))
job->last++;
}
while (--delta > 0);
}
}
}
// if there's been no jobs up to now, history must already be all zero
// so just remove history
if (job->jobsnum == 0 && job->last > 0)
job->last = 0;
// this also handles the issue of a job-less wipe with a high
// now->tv_usec and if the first job comes in during the next minute.
// without setting 'last=0' the previous empty full minute will
// always be included in the elapsed time used to calc the job rate
if (!locked)
mutex_unlock(&info->joblock);
}
// update info->job with a job as at 'now'
// info must be locked, wipe creates a new empty data set
static void add_gekko_job(struct COMPAC_INFO *info, struct timeval *now, bool wipe)
{
struct GEKKOJOB *job = &(info->job);
bool firstjob;
double avg;
double ms;
mutex_lock(&info->joblock);
job_offset(info, now, wipe, true);
if (!wipe)
{
if (job->jobnum[job->offset] == 0)
{
job->firstj[job->offset].tv_sec = now->tv_sec;
job->firstj[job->offset].tv_usec = now->tv_usec;
firstjob = true;
}
else
firstjob = false;
job->lastj[job->offset].tv_sec = now->tv_sec;
job->lastj[job->offset].tv_usec = now->tv_usec;
// first job time in each offset gets ignored
// this is only necessary for the very first job,
// but easier to do it for every offset group
if (firstjob)
{
// already true
// job->avgms[job->offset] = 0.0;
// job->minms[job->offset] = 0.0;
// job->maxms[job->offset] = 0.0;
}
else
{
avg = job->avgms[job->offset] * (double)(job->jobnum[job->offset] - 1);
ms = (double)(now->tv_sec - job->lastjob.tv_sec) * 1000.0;
ms += (double)(now->tv_usec - job->lastjob.tv_usec) / 1000.0;
// jobnum[] must be > 0
job->avgms[job->offset] = (avg + ms) / (double)(job->jobnum[job->offset]);
if (job->minms[job->offset] == 0.0)
{
job->minms[job->offset] = ms;
job->maxms[job->offset] = ms;
}
else
{
if (ms < job->minms[job->offset])
job->minms[job->offset] = ms;
if (job->maxms[job->offset] < ms)
job->maxms[job->offset] = ms;
}
}
(job->jobnum[job->offset])++;
(job->jobsnum)++;
job->lastjob.tv_sec = now->tv_sec;
job->lastjob.tv_usec = now->tv_usec;
}
mutex_unlock(&info->joblock);
}
// ignore nonces for this many work items after the ticket change
#define TICKET_DELAY 8
// allow this many nonces below the ticket value in case of work swap delays
// N.B. if the chip mask is half the wanted value,
// roughly 85% of shares will be low since CDF 190% = 0.850
// with the lowest nonce_count of 150 below for diff 2,
// TICKET_BLOW_LIM 4 will always be exceeded if incorrectly set to diff 1
#define TICKET_BELOW_LIM 4
struct TICKET_INFO {
uint32_t diff; // work diff value
uint32_t ticket_mask; // ticket mask to ensure work diff
int nonce_count; // CDF[Erl] nonces must have 1 below low_limit
double low_limit; // must be a diff below this or ticket is too hi
double hi_limit; // a diff below this means ticket is too low
// set to .1 below diff to avoid any rounding
uint32_t cclimit; // chips x cores limit i.e. required to go above 16
};
// ticket restart checks allowed before forced to diff=1
#define MAX_TICKET_CHECK 3
// ticket values, diff descending. List values rather than calc them
// end comments are how long at given task/sec (15 ~= 60 1diff nonce/sec = ~260GH/s)
// testing should take and chance of failure
// though it will retry MAX_TICKET_CHECK times so shouldn't give up in the
// exceedingly rare occasion where it fails once due to bad luck
// limit to max diff of 16 unless the chips x cores is a bit better than a GSF/GSFM
// to ensure enough nonces are coming back to identify status changes/issues
// the luck calculation is the chance all nonce diff values will be above low_limit
// after nonce_count nonces i.e. after nonce_count nonces there should be a nonce
// below low_limit, or the ticket mask is actually higher than it was set to
// the gsl function is cdf_gamma_Q(nonces, nonces, low_limit/diff)
static struct TICKET_INFO ticket_1397[] =
{
{ 64, 0xfc, 20000, 65.9, 63.9, 2600 }, // 90 59.3m Erlang=1.6x10-5 <- 64+ nonces
{ 32, 0xf8, 10000, 33.3, 31.9, 1300 }, // 45 29.6m Erlang=3.0x10-5 <- 32+ nonces
{ 16, 0xf0, 5000, 16.9, 15.9, 0 }, // 15 22.2m Erlang=4.6x10-5 <- 16+ nonces
{ 8, 0xe0, 1250, 8.9, 7.9, 0 }, // 15 166s Erlang=6.0x10-5
{ 4, 0xc0, 450, 4.9, 3.9, 0 }, // 15 30s Erlang=3.9x10-6
{ 2, 0x80, 150, 2.9, 1.9, 0 }, // 15 5s Erlang=5.4x10-7
{ 1, 0x00, 50, 1.9, 0.0, 0 }, // 15 0.8s Erlang=1.5x10-7 <- all nonces
{ 0 }
};
// force=true to allow setting it if it may not have taken before
// force also delays longer after sending the ticket mask
// diff=0.0 mean set the highest valid
static void set_ticket(struct cgpu_info *compac, float diff, bool force, bool locked)
{
struct COMPAC_INFO *info = compac->device_data;
struct timeval now;
bool got = false;
uint32_t udiff, new_diff = 0, new_mask = 0, cc;
int i;
if (diff == 0.0)
{
// above max will get the highest valid for cc
diff = 128;
}
// if (!force && info->last_work_diff == diff)
// return;
// closest uint diff equal or below
udiff = (uint32_t)floor(diff);
cc = info->chips * info->cores;
for (i = 0; ticket_1397[i].diff > 0; i++)
{
if (udiff >= ticket_1397[i].diff && cc > ticket_1397[i].cclimit)
{
// if ticket is already the same
if (!force && info->difficulty == ticket_1397[i].diff)
return;
if (!locked)
mutex_lock(&info->lock);
new_diff = info->difficulty = ticket_1397[i].diff;
new_mask = info->ticket_mask = ticket_1397[i].ticket_mask;
info->last_work_diff = diff;
cgtime(&info->last_ticket_attempt);
info->ticket_number = i;
info->ticket_work = 0;
info->ticket_nonces = 0;
info->below_nonces = 0;
info->ticket_ok = false;
info->ticket_got_low = false;
if (!locked)
mutex_unlock(&info->lock);
got = true;
break;
}
}
// code failure
if (!got)
return;
// set them all the same 0x51 .... 0x00
unsigned char ticket[] = {0x51, 0x09, 0x00, BM1397TICKET, 0x00, 0x00, 0x00, 0xC0, 0x00};
ticket[7] = info->ticket_mask;
compac_send2(compac, ticket, sizeof(ticket), 8 * sizeof(ticket) - 8, "ticket");
if (!force)
gekko_usleep(info, MS2US(10));
else
gekko_usleep(info, MS2US(20));
applog(LOG_ERR, "%d: %s %d - set ticket to 0x%02x/%u work %u/%.1f",
compac->cgminer_id, compac->drv->name, compac->device_id,
new_mask, new_diff, udiff, diff);
// wipe info->gh/asic->gc
cgtime(&now);
gh_offset(info, &now, true, false);
job_offset(info, &now, true, false);
// reset P:
info->frequency_computed = 0;
}
// expected nonces for GEKKOCHIP - MUST already be locked AND gc_offset()
// full 50 mins + current offset in 10 mins - N.B. uses CLOCK_MONOTONIC
// it will grow from 0% to ~100% between 50 & 60 mins if the chip
// is performing at 100% - random variance of course also applies
static double noncepercent(struct COMPAC_INFO *info, int chip, struct timeval *now)
{
double sec, hashpersec, noncepersec, nonceexpect;
if (info->asic_type != BM1397)
return 0.0;
sec = CHTIME * (CHNUM-1) + (now->tv_sec % CHTIME) + ((double)now->tv_usec / 1000000.0);
hashpersec = info->asics[chip].frequency * info->cores * info->hr_scale * 1000000.0;
noncepersec = (hashpersec / (double)0xffffffffull)
/ (double)(ticket_1397[info->ticket_number].diff);
nonceexpect = noncepersec * sec;
return 100.0 * (double)(info->asics[chip].gc.noncesum) / nonceexpect;
}
// GSF/GSFM any chip count
static void calc_gsf_freq(struct cgpu_info *compac, float frequency, int chip)
{
struct COMPAC_INFO *info = compac->device_data;
char chipn[8];
bool doall;
if (info->asic_type != BM1397)
return;
if (chip == -1)
doall = true;
else
{
if (chip < 0 || chip >= (int)(info->chips))
{
applog(LOG_ERR, "%d: %s %d - invalid set chip [%d] -> freq %.2fMHz",
compac->cgminer_id, compac->drv->name, compac->device_id, chip, frequency);
return;
}
doall = false;
}
// if attempting the same frequency that previously failed ...
if (frequency != 0 && frequency == info->freq_fail)
return;
unsigned char prefreqall[] = {0x51, 0x09, 0x00, 0x70, 0x0F, 0x0F, 0x0F, 0x00, 0x00};
unsigned char prefreqch[] = {0x41, 0x09, 0x00, 0x70, 0x0F, 0x0F, 0x0F, 0x00, 0x00};
// default 200Mhz if it fails
unsigned char freqbufall[] = {0x51, 0x09, 0x00, BM1397FREQ, 0x40, 0xF0, 0x02, 0x35, 0x00};
unsigned char freqbufch[] = {0x41, 0x09, 0x00, BM1397FREQ, 0x40, 0xF0, 0x02, 0x35, 0x00};
float deffreq = 200.0;
float fa, fb, fc1, fc2, newf;