seems to be working

This commit is contained in:
shufps 2024-09-09 15:10:08 +02:00
parent 0931960ac1
commit 4b7a6d8146
No known key found for this signature in database
GPG Key ID: 371CB8C24D8CB455
19 changed files with 487 additions and 350 deletions

View File

@ -17,6 +17,9 @@
#include "drivers/nerd-nos/bm1397.h"
#include "drivers/nerd-nos/serial.h"
#ifdef NERD_NOS
#include "mining_nerdnos.h"
#endif
#ifdef TOUCH_ENABLE
#include "TouchHandler.h"

View File

@ -7,6 +7,7 @@
#include "freertos/task.h"
#include "../devices/device.h"
#include "crc.h"
#include "utils.h"
#define TYPE_JOB 0x20
#define TYPE_CMD 0x40
@ -48,7 +49,6 @@ typedef struct __attribute__((__packed__))
static const char *TAG = "bm1397Module";
static uint8_t asic_response_buffer[CHUNK_SIZE];
static uint32_t prev_nonce = 0;
static task_result result;
uint32_t increment_bitmask(const uint32_t value, const uint32_t mask);
@ -101,7 +101,7 @@ static void _send_read_address(void)
{
unsigned char read_address[2] = {0x00, 0x00};
// send serial data
_send_BM1397((TYPE_CMD | GROUP_ALL | CMD_READ), read_address, 2, BM1937_SERIALTX_DEBUG);
_send_BM1397((TYPE_CMD | GROUP_ALL | CMD_READ), read_address, 2, BM1397_SERIALTX_DEBUG);
}
static void _send_chain_inactive(void)
@ -109,7 +109,7 @@ static void _send_chain_inactive(void)
unsigned char read_address[2] = {0x00, 0x00};
// send serial data
_send_BM1397((TYPE_CMD | GROUP_ALL | CMD_INACTIVE), read_address, 2, BM1937_SERIALTX_DEBUG);
_send_BM1397((TYPE_CMD | GROUP_ALL | CMD_INACTIVE), read_address, 2, BM1397_SERIALTX_DEBUG);
}
static void _set_chip_address(uint8_t chipAddr)
@ -117,7 +117,7 @@ static void _set_chip_address(uint8_t chipAddr)
unsigned char read_address[2] = {chipAddr, 0x00};
// send serial data
_send_BM1397((TYPE_CMD | GROUP_SINGLE | CMD_SETADDRESS), read_address, 2, BM1937_SERIALTX_DEBUG);
_send_BM1397((TYPE_CMD | GROUP_SINGLE | CMD_SETADDRESS), read_address, 2, BM1397_SERIALTX_DEBUG);
}
void BM1397_send_hash_frequency(float frequency)
@ -187,12 +187,12 @@ void BM1397_send_hash_frequency(float frequency)
for (i = 0; i < 2; i++)
{
vTaskDelay(10 / portTICK_PERIOD_MS);
_send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), prefreqall, 6, BM1937_SERIALTX_DEBUG);
_send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), prefreqall, 6, BM1397_SERIALTX_DEBUG);
}
for (i = 0; i < 2; i++)
{
vTaskDelay(10 / portTICK_PERIOD_MS);
_send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), freqbufall, 6, BM1937_SERIALTX_DEBUG);
_send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), freqbufall, 6, BM1397_SERIALTX_DEBUG);
}
vTaskDelay(10 / portTICK_PERIOD_MS);
@ -228,24 +228,24 @@ static uint8_t _send_init(uint64_t frequency, uint16_t asic_count)
}
unsigned char init[6] = {0x00, CLOCK_ORDER_CONTROL_0, 0x00, 0x00, 0x00, 0x00}; // init1 - clock_order_control0
_send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), init, 6, BM1937_SERIALTX_DEBUG);
_send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), init, 6, BM1397_SERIALTX_DEBUG);
unsigned char init2[6] = {0x00, CLOCK_ORDER_CONTROL_1, 0x00, 0x00, 0x00, 0x00}; // init2 - clock_order_control1
_send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), init2, 6, BM1937_SERIALTX_DEBUG);
_send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), init2, 6, BM1397_SERIALTX_DEBUG);
unsigned char init3[9] = {0x00, ORDERED_CLOCK_ENABLE, 0x00, 0x00, 0x00, 0x01}; // init3 - ordered_clock_enable
_send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), init3, 6, BM1937_SERIALTX_DEBUG);
_send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), init3, 6, BM1397_SERIALTX_DEBUG);
unsigned char init4[9] = {0x00, CORE_REGISTER_CONTROL, 0x80, 0x00, 0x80, 0x74}; // init4 - init_4_?
_send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), init4, 6, BM1937_SERIALTX_DEBUG);
_send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), init4, 6, BM1397_SERIALTX_DEBUG);
BM1397_set_job_difficulty_mask(BM1397_INITIAL_DIFFICULTY);
unsigned char init5[9] = {0x00, PLL3_PARAMETER, 0xC0, 0x70, 0x01, 0x11}; // init5 - pll3_parameter
_send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), init5, 6, BM1937_SERIALTX_DEBUG);
_send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), init5, 6, BM1397_SERIALTX_DEBUG);
unsigned char init6[9] = {0x00, FAST_UART_CONFIGURATION, 0x06, 0x00, 0x00, 0x0F}; // init6 - fast_uart_configuration
_send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), init6, 6, BM1937_SERIALTX_DEBUG);
_send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), init6, 6, BM1397_SERIALTX_DEBUG);
BM1397_set_default_baud();
@ -294,7 +294,7 @@ int BM1397_set_default_baud(void)
{
// default divider of 26 (11010) for 115,749
unsigned char baudrate[9] = {0x00, MISC_CONTROL, 0x00, 0x00, 0b01111010, 0b00110001}; // baudrate - misc_control
_send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), baudrate, 6, BM1937_SERIALTX_DEBUG);
_send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), baudrate, 6, BM1397_SERIALTX_DEBUG);
return 115749;
}
@ -304,7 +304,7 @@ int BM1397_set_max_baud(void)
ESP_LOGI(TAG, "Setting max baud of 3125000");
unsigned char baudrate[9] = {0x00, MISC_CONTROL, 0x00, 0x00, 0b01100000, 0b00110001};
; // baudrate - misc_control
_send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), baudrate, 6, BM1937_SERIALTX_DEBUG);
_send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), baudrate, 6, BM1397_SERIALTX_DEBUG);
return 3125000;
}
@ -317,7 +317,7 @@ void BM1397_set_job_difficulty_mask(int difficulty)
// The mask must be a power of 2 so there are no holes
// Correct: {0b00000000, 0b00000000, 0b11111111, 0b11111111}
// Incorrect: {0b00000000, 0b00000000, 0b11100111, 0b11111111}
difficulty = _largest_power_of_two(difficulty) - 1; // (difficulty - 1) if it is a pow 2 then step down to second largest for more hashrate sampling
difficulty = largest_power_of_two(difficulty) - 1; // (difficulty - 1) if it is a pow 2 then step down to second largest for more hashrate sampling
// convert difficulty into char array
// Ex: 256 = {0b00000000, 0b00000000, 0b00000000, 0b11111111}, {0x00, 0x00, 0x00, 0xff}
@ -329,18 +329,18 @@ void BM1397_set_job_difficulty_mask(int difficulty)
// So a mask of 512 looks like 0b00000000 00000000 00000001 1111111
// and not 0b00000000 00000000 10000000 1111111
job_difficulty_mask[5 - i] = _reverse_bits(value);
job_difficulty_mask[5 - i] = reverse_bits(value);
}
ESP_LOGI(TAG, "Setting job ASIC mask to %d", difficulty);
_send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), job_difficulty_mask, 6, BM1937_SERIALTX_DEBUG);
_send_BM1397((TYPE_CMD | GROUP_ALL | CMD_WRITE), job_difficulty_mask, 6, BM1397_SERIALTX_DEBUG);
}
void BM1397_send_work(bm_job_t *next_bm_job, uint8_t job_id)
{
job_packet job;
job_packet_t job;
// max job number is 128
// there is still some really weird logic with the job id bits for the asic to sort out
// so we have it limited to 128 and it has to increment by 4
@ -360,7 +360,7 @@ void BM1397_send_work(bm_job_t *next_bm_job, uint8_t job_id)
memcpy(job.midstate3, next_bm_job->midstate3, 32);
}
_send_BM1397((TYPE_JOB | GROUP_SINGLE | CMD_WRITE), (uint8_t*) &job, sizeof(job_packet), BM1397_DEBUG_WORK);
_send_BM1397((TYPE_JOB | GROUP_SINGLE | CMD_WRITE), (uint8_t*) &job, sizeof(job_packet_t), BM1397_DEBUG_WORK);
}
asic_result *BM1397_receive_work(uint16_t timeout)
@ -392,7 +392,6 @@ asic_result *BM1397_receive_work(uint16_t timeout)
task_result *BM1397_proccess_work(uint32_t version, uint16_t timeout)
{
asic_result *asic_result = BM1397_receive_work(timeout);
if (asic_result == NULL)
@ -401,9 +400,6 @@ task_result *BM1397_proccess_work(uint32_t version, uint16_t timeout)
return NULL;
}
uint8_t nonce_found = 0;
uint32_t first_nonce = 0;
uint8_t rx_job_id = (asic_result->job_id & 0xfc) >> 2;
uint8_t rx_midstate_index = asic_result->job_id & 0x03;
@ -413,29 +409,6 @@ task_result *BM1397_proccess_work(uint32_t version, uint16_t timeout)
rolled_version = increment_bitmask(rolled_version, 0x1fffe000);
}
// ASIC may return the same nonce multiple times
// or one that was already found
// most of the time it behavies however
if (nonce_found == 0)
{
first_nonce = asic_result->nonce;
nonce_found = 1;
}
else if (asic_result->nonce == first_nonce)
{
// stop if we've already seen this nonce
return NULL;
}
if (asic_result->nonce == prev_nonce)
{
return NULL;
}
else
{
prev_nonce = asic_result->nonce;
}
result.job_id = rx_job_id;
result.nonce = asic_result->nonce;
result.rolled_version = rolled_version;

View File

@ -1,49 +1,15 @@
#pragma once
#include <stdio.h>
#include "common.h"
#include "mining.h"
#include "nerdnos.h"
#include "crc.h"
#define CRC5_MASK 0x1F
#define BM1397_INITIAL_DIFFICULTY 64
#define BM1937_SERIALTX_DEBUG false
#define BM1937_SERIALRX_DEBUG true //false
#define BM1397_SERIALTX_DEBUG false
#define BM1397_SERIALRX_DEBUG true //false
#define BM1397_DEBUG_WORK false //causes insane amount of debug output
typedef struct
{
float frequency;
} bm1397Module;
typedef enum
{
JOB_PACKET = 0,
CMD_PACKET = 1,
} packet_type_t;
typedef enum
{
JOB_RESP = 0,
CMD_RESP = 1,
} response_type_t;
typedef struct __attribute__((__packed__))
{
uint8_t job_id;
uint8_t num_midstates;
uint8_t starting_nonce[4];
uint8_t nbits[4];
uint8_t ntime[4];
uint8_t merkle4[4];
uint8_t midstate[32];
uint8_t midstate1[32];
uint8_t midstate2[32];
uint8_t midstate3[32];
} job_packet;
uint8_t BM1397_init(uint64_t frequency, uint16_t asic_count);
void BM1397_send_work(bm_job_t * next_bm_job, uint8_t job_id);

View File

@ -1,41 +1,6 @@
#ifndef COMMON_H_
#define COMMON_H_
#pragma once
#include <stdint.h>
typedef struct __attribute__((__packed__))
{
uint8_t job_id;
uint32_t nonce;
uint32_t rolled_version;
} task_result;
static unsigned char _reverse_bits(unsigned char num)
{
unsigned char reversed = 0;
int i;
for (i = 0; i < 8; i++) {
reversed <<= 1; // Left shift the reversed variable by 1
reversed |= num & 1; // Use bitwise OR to set the rightmost bit of reversed to the current bit of num
num >>= 1; // Right shift num by 1 to get the next bit
}
return reversed;
}
static int _largest_power_of_two(int num)
{
int power = 0;
while (num > 1) {
num = num >> 1;
power++;
}
return 1 << power;
}
#include <stddef.h>
void printBufferHex(const char *prefix, const uint8_t* buf, size_t len);
#endif

View File

@ -1,18 +1,8 @@
#ifndef CRC_H_
#define CRC_H_
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
uint8_t crc5(uint8_t *buffer, uint16_t len);
uint16_t crc16(uint8_t *buffer, uint16_t len);
uint16_t crc16_false(uint8_t *buffer, uint16_t len);
#ifdef __cplusplus
}
#endif
#endif // CRC_H_

View File

@ -11,6 +11,7 @@
#include "utils.h"
#include "stratum.h"
#include "mbedtls/sha256.h"
#include "../../mining.h"
///////cgminer nonce testing
/* truediffone == 0x00000000FFFF0000000000000000000000000000000000000000000000000000
@ -18,7 +19,7 @@
static const double truediffone = 26959535291011309493156476344723991336010898738574164086137773096960.0;
/* testing a nonce and return the diff - 0 means invalid */
double asic_test_nonce_value(const bm_job_t *job, const uint32_t nonce, const uint32_t rolled_version)
double nerdnos_test_nonce_value(const bm_job_t *job, const uint32_t nonce, const uint32_t rolled_version, uint8_t hash_result[32])
{
double d64, s64, ds;
unsigned char header[80];
@ -38,7 +39,6 @@ double asic_test_nonce_value(const bm_job_t *job, const uint32_t nonce, const ui
memcpy(header + 76, &nonce, 4);
unsigned char hash_buffer[32];
unsigned char hash_result[32];
// double hash the header
mbedtls_sha256(header, 80, hash_buffer, 0);
@ -51,29 +51,7 @@ double asic_test_nonce_value(const bm_job_t *job, const uint32_t nonce, const ui
return ds;
}
uint32_t increment_bitmask(const uint32_t value, const uint32_t mask)
{
// if mask is zero, just return the original value
if (mask == 0)
return value;
uint32_t carry = (value & mask) + (mask & -mask); // increment the least significant bit of the mask
uint32_t overflow = carry & ~mask; // find overflowed bits that are not in the mask
uint32_t new_value = (value & ~mask) | (carry & mask); // set bits according to the mask
// Handle carry propagation
if (overflow > 0)
{
uint32_t carry_mask = (overflow << 1); // shift left to get the mask where carry should be propagated
new_value = increment_bitmask(new_value, carry_mask); // recursively handle carry propagation
}
return new_value;
}
void calculate_merkle_root_hash(const char *coinbase_tx, mining_job* job, char merkle_root_hash[65])
static void calculate_merkle_root_hash(const char *coinbase_tx, mining_job* job, char merkle_root_hash[65])
{
size_t coinbase_tx_bin_len = strlen(coinbase_tx) / 2;
uint8_t coinbase_tx_bin[coinbase_tx_bin_len];
@ -93,7 +71,7 @@ void calculate_merkle_root_hash(const char *coinbase_tx, mining_job* job, char m
}
// take a mining_notify struct with ascii hex strings and convert it to a bm_job struct
void construct_bm_job(mining_job *job, const char *merkle_root, uint32_t version_mask, bm_job_t *new_job)
static void construct_bm_job(mining_job *job, const char *merkle_root, uint32_t version_mask, bm_job_t *new_job)
{
new_job->version = strtoul(job->version.c_str(), NULL, 16);
new_job->target = strtoul(job->nbits.c_str(), NULL, 16);
@ -139,7 +117,7 @@ void construct_bm_job(mining_job *job, const char *merkle_root, uint32_t version
new_job->num_midstates = 4;
}
void asic_create_job(mining_subscribe *mWorker, mining_job *job, bm_job_t *next_job, uint32_t extranonce_2) {
void nerdnos_create_job(mining_subscribe *mWorker, mining_job *job, bm_job_t *next_job, uint32_t extranonce_2, uint32_t stratum_difficulty) {
char extranonce_2_str[mWorker->extranonce2_size * 2 + 1]; // +1 zero termination
snprintf(extranonce_2_str, sizeof(extranonce_2_str), "%0*lx", (int) mWorker->extranonce2_size * 2, extranonce_2);
@ -156,20 +134,24 @@ void asic_create_job(mining_subscribe *mWorker, mining_job *job, bm_job_t *next_
next_job->jobid = strdup(job->job_id.c_str());
next_job->extranonce2 = strdup(extranonce_2_str);
//next_job->pool_diff = stratum_difficulty;
next_job->pool_diff = stratum_difficulty;
}
void asic_send_work(bm_job_t *next_bm_job, uint8_t job_id) {
void nerdnos_send_work(bm_job_t *next_bm_job, uint8_t job_id) {
BM1397_send_work(next_bm_job, job_id);
}
task_result *asic_proccess_work(uint32_t version, uint16_t timeout) {
task_result *nerdnos_proccess_work(uint32_t version, uint16_t timeout) {
return BM1397_proccess_work(version, timeout);
}
void asic_free_bm_job(bm_job_t *job) {
void nerdnos_free_bm_job(bm_job_t *job) {
free(job->jobid);
free(job->extranonce2);
// mark as free
job->ntime = 0;
}
void nerdnos_set_asic_difficulty(uint32_t difficulty) {
BM1397_set_job_difficulty_mask(difficulty);
}

View File

@ -1,33 +1,25 @@
#pragma once
#include <stdint.h>
#include "common.h"
//#include "stratum.h"
#include "nerdnos.h"
// forward declaration to resolve circular inclusions
typedef struct mining_subscribe mining_subscribe;
typedef struct mining_job mining_job;
typedef struct
{
uint32_t version;
uint32_t version_mask;
uint8_t prev_block_hash[32];
uint8_t prev_block_hash_be[32];
uint8_t merkle_root[32];
uint8_t merkle_root_be[32];
uint32_t ntime;
uint32_t target; // aka difficulty, aka nbits
uint32_t starting_nonce;
// set the asic hardware difficulty
void nerdnos_set_asic_difficulty(uint32_t current_difficulty);
uint8_t num_midstates;
uint8_t midstate[32];
uint8_t midstate1[32];
uint8_t midstate2[32];
uint8_t midstate3[32];
char *jobid;
char *extranonce2;
} bm_job_t;
// create asic job
void nerdnos_create_job(mining_subscribe *mWorker, mining_job *job, bm_job_t *next_job, uint32_t extranonce_2, uint32_t stratum_difficulty);
// send new work to the asic
void nerdnos_send_work(bm_job_t *next_bm_job, uint8_t job_id);
void asic_send_work(bm_job_t *next_bm_job, uint8_t job_id);
task_result *asic_proccess_work(uint32_t version, uint16_t timeout);
// receive and process responses
task_result *nerdnos_proccess_work(uint32_t version, uint16_t timeout);
double asic_test_nonce_value(const bm_job_t *job, const uint32_t nonce, const uint32_t rolled_version);
void asic_free_bm_job(bm_job_t *job);
// test difficulty
double nerdnos_test_nonce_value(const bm_job_t *job, const uint32_t nonce, const uint32_t rolled_version, uint8_t hash_result[32]);
// free allocated RAM for strings on bm_job_t
void nerdnos_free_bm_job(bm_job_t *job);

View File

@ -0,0 +1,66 @@
#pragma once
#include <stdint.h>
#define BM1397_INITIAL_DIFFICULTY 128
typedef struct __attribute__((__packed__))
{
uint8_t job_id;
uint32_t nonce;
uint32_t rolled_version;
} task_result;
typedef struct
{
uint32_t version;
uint32_t version_mask;
uint8_t prev_block_hash[32];
uint8_t prev_block_hash_be[32];
uint8_t merkle_root[32];
uint8_t merkle_root_be[32];
uint32_t ntime;
uint32_t target; // aka difficulty, aka nbits
uint32_t starting_nonce;
uint8_t num_midstates;
uint8_t midstate[32];
uint8_t midstate1[32];
uint8_t midstate2[32];
uint8_t midstate3[32];
char *jobid;
char *extranonce2;
uint32_t pool_diff;
} bm_job_t;
/*
typedef struct
{
float frequency;
} bm1397Module;
*/
typedef enum
{
JOB_PACKET = 0,
CMD_PACKET = 1,
} packet_type_t;
typedef enum
{
JOB_RESP = 0,
CMD_RESP = 1,
} response_type_t;
typedef struct __attribute__((__packed__))
{
uint8_t job_id;
uint8_t num_midstates;
uint8_t starting_nonce[4];
uint8_t nbits[4];
uint8_t ntime[4];
uint8_t merkle4[4];
uint8_t midstate[32];
uint8_t midstate1[32];
uint8_t midstate2[32];
uint8_t midstate3[32];
} job_packet_t;

View File

@ -51,12 +51,24 @@ int SERIAL_send(uint8_t *data, int len, bool debug)
return uart_write_bytes(UART_NUM_1, (const char *)data, len);
}
int SERIAL_check_for_data() {
int length;
uart_get_buffered_data_len(UART_NUM_1, (size_t*)&length);
return length;
}
/// @brief waits for a serial response from the device
/// @param buf buffer to read data into
/// @param buf number of ms to wait before timing out
/// @return number of bytes read, or -1 on error
int16_t SERIAL_rx(uint8_t *buf, uint16_t size, uint16_t timeout_ms)
{
// don't return incomplete data
if (SERIAL_check_for_data() < size) {
return 0;
}
int16_t bytes_read = uart_read_bytes(UART_NUM_1, buf, size, timeout_ms / portTICK_PERIOD_MS);
// if (bytes_read > 0) {
// printf("rx: ");

View File

@ -1,5 +1,4 @@
#ifndef SERIAL_H_
#define SERIAL_H_
#pragma once
#define CHUNK_SIZE 1024
@ -10,4 +9,3 @@ int16_t SERIAL_rx(uint8_t *, uint16_t, uint16_t);
void SERIAL_clear_buffer(void);
void SERIAL_set_baud(int baud);
#endif /* SERIAL_H_ */

View File

@ -306,4 +306,52 @@ uint32_t flip32(uint32_t val)
ret |= (val & 0xFF0000) >> 8;
ret |= (val & 0xFF000000) >> 24;
return ret;
}
unsigned char reverse_bits(unsigned char num)
{
unsigned char reversed = 0;
int i;
for (i = 0; i < 8; i++) {
reversed <<= 1; // Left shift the reversed variable by 1
reversed |= num & 1; // Use bitwise OR to set the rightmost bit of reversed to the current bit of num
num >>= 1; // Right shift num by 1 to get the next bit
}
return reversed;
}
int largest_power_of_two(int num)
{
int power = 0;
while (num > 1) {
num = num >> 1;
power++;
}
return 1 << power;
}
uint32_t increment_bitmask(const uint32_t value, const uint32_t mask)
{
// if mask is zero, just return the original value
if (mask == 0)
return value;
uint32_t carry = (value & mask) + (mask & -mask); // increment the least significant bit of the mask
uint32_t overflow = carry & ~mask; // find overflowed bits that are not in the mask
uint32_t new_value = (value & ~mask) | (carry & mask); // set bits according to the mask
// Handle carry propagation
if (overflow > 0)
{
uint32_t carry_mask = (overflow << 1); // shift left to get the mask where carry should be propagated
new_value = increment_bitmask(new_value, carry_mask); // recursively handle carry propagation
}
return new_value;
}

View File

@ -1,4 +1,4 @@
#pragma one
#pragma once
#include <stddef.h>
#include <stdint.h>
@ -33,3 +33,8 @@ void prettyHex(unsigned char *buf, int len);
uint32_t flip32(uint32_t val);
unsigned char reverse_bits(unsigned char num);
int largest_power_of_two(int num);
uint32_t increment_bitmask(const uint32_t value, const uint32_t mask);

View File

@ -14,11 +14,6 @@
#include "drivers/displays/display.h"
#include "drivers/storage/storage.h"
#ifdef NERD_NOS
#include "drivers/nerd-nos/mining.h"
#endif
nvs_handle_t stat_handle;
uint32_t templates = 0;
@ -41,8 +36,8 @@ extern TSettings Settings;
IPAddress serverIP(1, 1, 1, 1); //Temporally save poolIPaddres
//Global work data
static WiFiClient client;
static miner_data mMiner; //Global miner data (Create a miner class TODO)
WiFiClient client;
miner_data mMiner; //Global miner data (Create a miner class TODO)
mining_subscribe mWorker;
mining_job mJob;
monitor_data mMonitor;
@ -103,12 +98,14 @@ bool checkPoolInactivity(unsigned int keepAliveTime, unsigned long inactivityTim
}*/
}
#ifndef NERD_NOS
if(elapsedKHs == 0){
//Check if hashrate is 0 during inactivityTIme
if(mStart0Hashrate == 0) mStart0Hashrate = millis();
if((millis()-mStart0Hashrate) > inactivityTime) { mStart0Hashrate=0; return true;}
return false;
}
#endif
mStart0Hashrate = 0;
return false;
@ -372,121 +369,6 @@ void runMiner(void * task_id) {
}
}
void asic_create_job(mining_subscribe *mWorker, mining_job *job, bm_job_t *next_job, uint32_t extranonce_2);
// we can have 32 different job ids
bm_job_t asic_jobs[32];
void runASIC(void * task_id) {
Serial.printf("[MINER] Started runASIC Task!\n");
uint32_t extranonce_2 = 0;
while(1) {
// wait for new job
while(1) {
if (mMiner.newJob == true) {
break;
}
vTaskDelay(100 / portTICK_PERIOD_MS); //Small delay
}
if(mMiner.newJob) {
mMiner.newJob = false; //Clear newJob flag
}
mMiner.inRun = true; //Set inRun flag
Serial.println(">>> STARTING TO HASH NONCES");
uint32_t startT = micros();
memset(asic_jobs, 0, sizeof(asic_jobs));
// we are assuming the version doesn't change from job to job
uint32_t version = strtoul(mJob.version.c_str(), NULL, 16);
mMonitor.NerdStatus = NM_hashing;
while (mMiner.inRun) {
extranonce_2++;
// use extranonce2 as job id
uint8_t asic_job_id = (uint8_t) (extranonce_2 % 32);
// if it was used before, we have to free the pointers
if (asic_jobs[asic_job_id].ntime) {
asic_free_bm_job(&asic_jobs[asic_job_id]);
}
// create the next asic job
asic_create_job(&mWorker, &mJob, &asic_jobs[asic_job_id], extranonce_2);
// send the job and
asic_send_work(&asic_jobs[asic_job_id], asic_job_id);
// wait 30ms for the response
// the pointer returned is the RS232 receive buffer :shushing-face:
// but we only have a single thread so it should be okay
task_result *result = asic_proccess_work(version, 30);
// if we haven't received anything in time, so send a new job
if (!result) {
continue;
}
// if we have received a job we don't know
if (!asic_jobs[result->job_id].ntime) {
Serial.printf("No Job found for received ID %02x\n", result->job_id);
continue;
}
// check the nonce difficulty
double diff_hash = asic_test_nonce_value(
&asic_jobs[result->job_id],
result->nonce,
result->rolled_version);
Serial.print(" - Current diff share: "); Serial.println(diff_hash, 12);
Serial.print(" - Current pool diff : "); Serial.println(mMiner.poolDifficulty, 12);
// update best diff
if (diff_hash > best_diff)
best_diff = diff_hash;
if(diff_hash > mMiner.poolDifficulty)
{
tx_mining_submit_asic(client, mWorker, &asic_jobs[result->job_id], result);
Serial.println("valid share!");
/*
Serial.print(" - Current diff share: "); Serial.println(diff_hash,12);
Serial.print(" - Current pool diff : "); Serial.println(mMiner.poolDifficulty,12);
*/
/*
Serial.print(" - TX SHARE: ");
for (size_t i = 0; i < 32; i++)
Serial.printf("%02x", hash[i]);
*/
#ifdef DEBUG_MINING
Serial.println("");
Serial.print(" - Current nonce: "); Serial.println(nonce);
Serial.print(" - Current block header: ");
for (size_t i = 0; i < 80; i++) {
Serial.printf("%02x", mMiner.bytearray_blockheader[i]);
}
#endif
Serial.println("");
mLastTXtoPool = millis();
}
}
Serial.println ("MINER WORK ABORTED >> waiting new job");
mMiner.inRun = false;
uint32_t duration = micros() - startT;
/*
if (esp_task_wdt_reset() == ESP_OK)
Serial.print(">>> Resetting watchdog timer");
*/
}
}
#define DELAY 100
#define REDRAW_EVERY 10

View File

@ -20,7 +20,7 @@ String printLocalTime(void);
void resetStat();
typedef struct{
typedef struct {
uint8_t bytearray_target[32];
uint8_t bytearray_pooltarget[32];
uint8_t merkle_result[32];

232
src/mining_nerdnos.cpp Normal file
View File

@ -0,0 +1,232 @@
#include <Arduino.h>
#include <ArduinoJson.h>
#include <WiFi.h>
#include <esp_task_wdt.h>
#include <nvs_flash.h>
#include <nvs.h>
//#include "ShaTests/nerdSHA256.h"
#include "ShaTests/nerdSHA256plus.h"
#include "stratum.h"
#include "mining.h"
#include "utils.h"
#include "monitor.h"
#include "timeconst.h"
#include "drivers/displays/display.h"
#include "drivers/storage/storage.h"
#include "drivers/nerd-nos/nerdnos.h"
#include "mining_nerdnos.h"
extern WiFiClient client;
extern mining_subscribe mWorker;
extern mining_job mJob;
extern miner_data mMiner;
extern monitor_data mMonitor;
extern pthread_mutex_t job_mutex;
extern double best_diff;
extern unsigned long mLastTXtoPool;
// to track the jobs
// we can have 32 different job ids
static bm_job_t asic_jobs[32] = {0};
// to track hashrate
#define ASIC_HISTORY_SIZE 128
typedef struct {
uint32_t diffs[ASIC_HISTORY_SIZE];
uint32_t timestamps[ASIC_HISTORY_SIZE];
uint32_t newest;
uint32_t oldest;
uint64_t sum;
double avg_gh;
double duration;
int shares;
} history_t;
static pthread_mutex_t job_interval_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t job_interval_cond = PTHREAD_COND_INITIALIZER;
history_t history = {0};
double nerdnos_get_avg_hashrate() {
return history.avg_gh;
}
// incremental ringbuffer based hashrate calculation
static void calculate_hashrate(history_t *history, uint32_t diff) {
// if we have wrapped around at least once our ringbuffer is full
// and we have to remove the oldest element
if (history->newest + 1 >= ASIC_HISTORY_SIZE) {
history->sum -= history->diffs[history->oldest % ASIC_HISTORY_SIZE];
history->oldest++;
}
// add and store the newest sample
history->sum += diff;
history->diffs[history->newest % ASIC_HISTORY_SIZE] = diff;
history->timestamps[history->newest % ASIC_HISTORY_SIZE] = micros();
uint64_t oldest_timestamp = history->timestamps[history->oldest % ASIC_HISTORY_SIZE];
uint64_t newest_timestamp = history->timestamps[history->newest % ASIC_HISTORY_SIZE];
history->duration = (double) (newest_timestamp - oldest_timestamp) / 1.0e6;
history->shares = (int) history->newest - (int) history->oldest + 1;
if (history->duration) {
double avg = (double) (history->sum << 32llu) / history->duration;
history->avg_gh = avg / 1.0e9;
}
history->newest++;
}
// triggers the job creation
static void create_job_timer(TimerHandle_t xTimer)
{
pthread_mutex_lock(&job_interval_mutex);
pthread_cond_signal(&job_interval_cond);
pthread_mutex_unlock(&job_interval_mutex);
}
void runASIC(void * task_id) {
Serial.printf("[MINER] Started runASIC Task!\n");
// Create the timer
TimerHandle_t job_timer = xTimerCreate("NERDNOS_Job_Timer", NERDNOS_JOB_INTERVAL_MS / portTICK_PERIOD_MS, pdTRUE, NULL, create_job_timer);
if (job_timer == NULL) {
Serial.println("Failed to create NERNOS timer");
return;
}
// Start the timer
if (xTimerStart(job_timer, 0) != pdPASS) {
Serial.println("Failed to start NERDNOS timer");
return;
}
uint32_t extranonce_2 = 0;
while(1) {
// wait for new job
while(1) {
if (mMiner.newJob == true) {
break;
}
vTaskDelay(100 / portTICK_PERIOD_MS); //Small delay
}
if(mMiner.newJob) {
mMiner.newJob = false; //Clear newJob flag
}
mMiner.inRun = true; //Set inRun flag
Serial.println(">>> STARTING TO HASH NONCES");
uint32_t startT = micros();
memset(asic_jobs, 0, sizeof(asic_jobs));
// we are assuming the version doesn't change from job to job
uint32_t version = strtoul(mJob.version.c_str(), NULL, 16);
mMonitor.NerdStatus = NM_hashing;
uint32_t current_difficulty = 0;
while (mMiner.inRun) {
// wait for the timer to start a new job
// also yields the CPU
pthread_mutex_lock(&job_interval_mutex);
pthread_cond_wait(&job_interval_cond, &job_interval_mutex);
pthread_mutex_unlock(&job_interval_mutex);
// increment extranonce2
extranonce_2++;
// use extranonce2 as job id
uint8_t asic_job_id = (uint8_t) (extranonce_2 % 32);
// if it was used before, we have to free the pointers
if (asic_jobs[asic_job_id].ntime) {
nerdnos_free_bm_job(&asic_jobs[asic_job_id]);
}
// create the next asic job
// make sure that another task doesn't mess with the data while
// we are using it
pthread_mutex_lock(&job_mutex);
if (current_difficulty != mMiner.poolDifficulty) {
current_difficulty = mMiner.poolDifficulty;
nerdnos_set_asic_difficulty(current_difficulty);
Serial.printf("Set difficulty to %llu\n", current_difficulty);
}
nerdnos_create_job(&mWorker, &mJob, &asic_jobs[asic_job_id], extranonce_2, current_difficulty);
pthread_mutex_unlock(&job_mutex);
// send the job and
nerdnos_send_work(&asic_jobs[asic_job_id], asic_job_id);
// the pointer returned is the RS232 receive buffer :shushing-face:
// but we only have a single thread so it should be okay
// process all results if we have more than one
// this is okay because serial uses a buffer and (most likely^^) DMA
task_result *result = 0;
while (result = nerdnos_proccess_work(version, 1), result != NULL) {
// if we have received a job we don't know
if (!asic_jobs[result->job_id].ntime) {
Serial.printf("No Job found for received ID %02x\n", result->job_id);
continue;
}
uint8_t hash[32];
// check the nonce difficulty
double diff_hash = nerdnos_test_nonce_value(
&asic_jobs[result->job_id],
result->nonce,
result->rolled_version,
hash);
// update best diff
if (diff_hash > best_diff)
best_diff = diff_hash;
// calculate the hashrate
if (diff_hash >= asic_jobs[result->job_id].pool_diff) {
calculate_hashrate(&history, asic_jobs[result->job_id].pool_diff);
Serial.printf("avg hashrate: %.2fGH/s (history spans %2.fs, %d shares)\n", history.avg_gh, history.duration, history.shares);
}
if(diff_hash > mMiner.poolDifficulty)
{
tx_mining_submit_asic(client, mWorker, &asic_jobs[result->job_id], result);
Serial.println("valid share!");
Serial.print(" - Current diff share: "); Serial.println(diff_hash,12);
Serial.print(" - Current pool diff : "); Serial.println(mMiner.poolDifficulty,12);
Serial.print(" - TX SHARE: ");
for (size_t i = 0; i < 32; i++)
Serial.printf("%02x", hash[i]);
#ifdef DEBUG_MINING
Serial.println("");
Serial.print(" - Current nonce: "); Serial.println(nonce);
Serial.print(" - Current block header: ");
for (size_t i = 0; i < 80; i++) {
Serial.printf("%02x", mMiner.bytearray_blockheader[i]);
}
#endif
Serial.println("");
mLastTXtoPool = millis();
}
}
}
Serial.println ("MINER WORK ABORTED >> waiting new job");
mMiner.inRun = false;
uint32_t duration = micros() - startT;
/*
if (esp_task_wdt_reset() == ESP_OK)
Serial.print(">>> Resetting watchdog timer");
*/
}
}

6
src/mining_nerdnos.h Normal file
View File

@ -0,0 +1,6 @@
#pragma once
#define NERDNOS_JOB_INTERVAL_MS 30
void runASIC(void * task_id);
double nerdnos_get_avg_hashrate();

View File

@ -9,6 +9,10 @@
#include "monitor.h"
#include "drivers/storage/storage.h"
#ifdef NERD_NOS
#include "mining_nerdnos.h"
#endif
extern uint32_t templates;
extern uint32_t hashes;
extern uint32_t Mhashes;
@ -24,7 +28,7 @@ extern double best_diff; // track best diff
extern monitor_data mMonitor;
//from saved config
extern TSettings Settings;
extern TSettings Settings;
bool invertColors = false;
WiFiUDP ntpUDP;
@ -40,7 +44,7 @@ void setup_monitor(void){
/******** TIME ZONE SETTING *****/
timeClient.begin();
// Adjust offset depending on your zone
// GMT +2 in seconds (zona horaria de Europa Central)
timeClient.setTimeOffset(3600 * Settings.Timezone);
@ -55,11 +59,11 @@ void setup_monitor(void){
unsigned long mGlobalUpdate =0;
void updateGlobalData(void){
if((mGlobalUpdate == 0) || (millis() - mGlobalUpdate > UPDATE_Global_min * 60 * 1000)){
if (WiFi.status() != WL_CONNECTED) return;
//Make first API call to get global hash and current difficulty
HTTPClient http;
try {
@ -68,7 +72,7 @@ void updateGlobalData(void){
if (httpCode == HTTP_CODE_OK) {
String payload = http.getString();
DynamicJsonDocument doc(1024);
deserializeJson(doc, payload);
String temp = "";
@ -86,14 +90,14 @@ void updateGlobalData(void){
}
http.end();
//Make third API call to get fees
http.begin(getFees);
httpCode = http.GET();
if (httpCode == HTTP_CODE_OK) {
String payload = http.getString();
DynamicJsonDocument doc(1024);
deserializeJson(doc, payload);
String temp = "";
@ -108,7 +112,7 @@ void updateGlobalData(void){
mGlobalUpdate = millis();
}
http.end();
} catch(...) {
http.end();
@ -119,11 +123,11 @@ void updateGlobalData(void){
unsigned long mHeightUpdate = 0;
String getBlockHeight(void){
if((mHeightUpdate == 0) || (millis() - mHeightUpdate > UPDATE_Height_min * 60 * 1000)){
if (WiFi.status() != WL_CONNECTED) return current_block;
HTTPClient http;
try {
http.begin(getHeightAPI);
@ -136,24 +140,24 @@ String getBlockHeight(void){
current_block = payload;
mHeightUpdate = millis();
}
}
http.end();
} catch(...) {
http.end();
}
}
return current_block;
}
unsigned long mBTCUpdate = 0;
String getBTCprice(void){
if((mBTCUpdate == 0) || (millis() - mBTCUpdate > UPDATE_BTC_min * 60 * 1000)){
if (WiFi.status() != WL_CONNECTED) return (String(bitcoin_price) + "$");
HTTPClient http;
try {
http.begin(getBTCAPI);
@ -170,13 +174,13 @@ String getBTCprice(void){
mBTCUpdate = millis();
}
http.end();
} catch(...) {
http.end();
}
}
return (String(bitcoin_price) + "$");
}
@ -186,7 +190,7 @@ unsigned long initialTime = 0;
unsigned long mPoolUpdate = 0;
void getTime(unsigned long* currentHours, unsigned long* currentMinutes, unsigned long* currentSeconds){
//Check if need an NTP call to check current time
if((mTriggerUpdate == 0) || (millis() - mTriggerUpdate > UPDATE_PERIOD_h * 60 * 60 * 1000)){ //60 sec. * 60 min * 1000ms
if(WiFi.status() == WL_CONNECTED) {
@ -206,7 +210,7 @@ void getTime(unsigned long* currentHours, unsigned long* currentMinutes, unsigne
}
String getDate(){
unsigned long elapsedTime = (millis() - mTriggerUpdate) / 1000; // Tiempo transcurrido en segundos
unsigned long currentTime = initialTime + elapsedTime; // La hora actual
@ -229,15 +233,22 @@ String getTime(void){
char LocalHour[10];
sprintf(LocalHour, "%02d:%02d", currentHours, currentMinutes);
String mystring(LocalHour);
return LocalHour;
}
#ifdef NERD_NOS
String getCurrentHashRate(unsigned long mElapsed) {
// we have too little space for 2 digits after the decimal point
return String(nerdnos_get_avg_hashrate(), 1);
}
#else
String getCurrentHashRate(unsigned long mElapsed)
{
return String((1.0 * (elapsedKHs * 1000)) / mElapsed, 2);
}
#endif
mining_data getMiningData(unsigned long mElapsed)
{
@ -328,7 +339,7 @@ String getPoolAPIUrl(void) {
poolAPIUrl = String(getPublicPool);
if (Settings.PoolAddress == "public-pool.io") {
poolAPIUrl = "https://public-pool.io:40557/api/client/";
}
}
else {
if (Settings.PoolAddress == "nerdminers.org") {
poolAPIUrl = "https://pool.nerdminers.org/users/";
@ -354,13 +365,13 @@ String getPoolAPIUrl(void) {
}
pool_data getPoolData(void){
//pool_data pData;
if((mPoolUpdate == 0) || (millis() - mPoolUpdate > UPDATE_POOL_min * 60 * 1000)){
if (WiFi.status() != WL_CONNECTED) return pData;
//pool_data pData;
if((mPoolUpdate == 0) || (millis() - mPoolUpdate > UPDATE_POOL_min * 60 * 1000)){
if (WiFi.status() != WL_CONNECTED) return pData;
//Make first API call to get global hash and current difficulty
HTTPClient http;
http.setReuse(true);
try {
http.setReuse(true);
try {
String btcWallet = Settings.BtcWallet;
// Serial.println(btcWallet);
if (btcWallet.indexOf(".")>0) btcWallet = btcWallet.substring(0,btcWallet.indexOf("."));
@ -397,16 +408,16 @@ pool_data getPoolData(void){
double temp;
if (doc.containsKey("bestDifficulty")) {
temp = doc["bestDifficulty"].as<double>();
temp = doc["bestDifficulty"].as<double>();
char best_diff_string[16] = {0};
suffix_string(temp, best_diff_string, 16, 0);
pData.bestDifficulty = String(best_diff_string);
}
doc.clear();
mPoolUpdate = millis();
Serial.println("\n####### Pool Data OK!");
Serial.println("\n####### Pool Data OK!");
} else {
Serial.println("\n####### Pool Data HTTP Error!");
Serial.println("\n####### Pool Data HTTP Error!");
/* Serial.println(httpCode);
String payload = http.getString();
Serial.println(payload); */
@ -415,18 +426,18 @@ pool_data getPoolData(void){
pData.workersHash = "E";
pData.workersCount = 0;
http.end();
return pData;
return pData;
}
http.end();
} catch(...) {
Serial.println("####### Pool Error!");
Serial.println("####### Pool Error!");
// mPoolUpdate = millis();
pData.bestDifficulty = "P";
pData.workersHash = "Error";
pData.workersCount = 0;
http.end();
return pData;
}
}
}
return pData;
}

View File

@ -10,7 +10,9 @@
#include "utils.h"
#include "version.h"
#include <pthread.h>
pthread_mutex_t job_mutex = PTHREAD_MUTEX_INITIALIZER;
StaticJsonDocument<BUFFER_JSON_DOC> doc;
unsigned long id = 1;
@ -99,9 +101,11 @@ bool parse_mining_subscribe(String line, mining_subscribe& mSubscribe)
if (error || checkError(doc)) return false;
if (!doc.containsKey("result")) return false;
pthread_mutex_lock(&job_mutex);
mSubscribe.sub_details = String((const char*) doc["result"][0][0][1]);
mSubscribe.extranonce1 = String((const char*) doc["result"][1]);
mSubscribe.extranonce2_size = doc["result"][2];
pthread_mutex_unlock(&job_mutex);
return true;
}
@ -179,6 +183,7 @@ bool parse_mining_notify(String line, mining_job& mJob)
if (error) return false;
if (!doc.containsKey("params")) return false;
pthread_mutex_lock(&job_mutex);
mJob.job_id = String((const char*) doc["params"][0]);
mJob.prev_block_hash = String((const char*) doc["params"][1]);
mJob.coinb1 = String((const char*) doc["params"][2]);
@ -188,6 +193,7 @@ bool parse_mining_notify(String line, mining_job& mJob)
mJob.nbits = String((const char*) doc["params"][6]);
mJob.ntime = String((const char*) doc["params"][7]);
mJob.clean_jobs = doc["params"][8]; //bool
pthread_mutex_unlock(&job_mutex);
#ifdef DEBUG_MINING
Serial.print(" job_id: "); Serial.println(mJob.job_id);

View File

@ -16,7 +16,7 @@
#define BUFFER_JSON_DOC 4096
#define BUFFER 1024
typedef struct {
typedef struct mining_subscribe {
String sub_details;
String extranonce1;
String extranonce2;
@ -25,7 +25,7 @@ typedef struct {
char wPass[20];
} mining_subscribe;
typedef struct {
typedef struct mining_job {
String job_id;
String prev_block_hash;
String coinb1;