blob: 1773d9ec8795a90c6c49b898fba20e4c63c402ff [file] [log] [blame]
// SPDX-FileCopyrightText: 2020 Efabless Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// SPDX-License-Identifier: Apache-2.0
`default_nettype none
/*
*-------------------------------------------------------------
*
* btc_miner_top
* TODO!
*
* This is an example of a (trivially simple) user project,
* showing how the user project can connect to the logic
* analyzer, the wishbone bus, and the I/O pads.
*
* This project generates an integer count, which is output
* on the user area GPIO pads (digital output only). The
* wishbone connection allows the project to be controlled
* (start and stop) from the management SoC program.
*
* See the testbenches in directory "mprj_counter" for the
* example programs that drive this user project. The three
* testbenches are "io_ports", "la_test1", and "la_test2".
*
*-------------------------------------------------------------
*/
module btc_miner_top #(
parameter BITS = 32
)(
`ifdef USE_POWER_PINS
inout vccd1, // User area 1 1.8V supply
inout vssd1, // User area 1 digital ground
`endif
// Wishbone Slave ports (WB MI A)
input wb_clk_i,
input wb_rst_i,
input wbs_stb_i,
input wbs_cyc_i,
input wbs_we_i,
input [3:0] wbs_sel_i,
input [31:0] wbs_dat_i,
input [31:0] wbs_adr_i,
output wbs_ack_o,
output [31:0] wbs_dat_o,
// Logic Analyzer Signals
input [127:0] la_data_in,
output [127:0] la_data_out,
input [127:0] la_oenb,
// IOs
input [`MPRJ_IO_PADS-1:0] io_in,
output [`MPRJ_IO_PADS-1:0] io_out,
output [`MPRJ_IO_PADS-1:0] io_oeb,
// IRQ
output [2:0] irq
);
wire clk;
wire rst;
wire [`MPRJ_IO_PADS-1:0] io_in;
wire [`MPRJ_IO_PADS-1:0] io_out;
wire [`MPRJ_IO_PADS-1:0] io_oeb;
// WB wires
wire [31:0] rdata;
wire [31:0] wdata;
wire valid;
wire [3:0] wstrb;
// LA wires
wire [31:0] la_write0;
wire [31:0] la_write1;
wire [31:0] la_write2;
wire [31:0] la_write3;
// Bitcoin mining variables
wire [BITS-1:0] nonce;
wire idle;
wire [639:0] block_header;
wire [255:0] target;
// TODO use top 32-bits of LA to control muxing and other variables like starting state machine
wire [2:0] la_sel;
assign la_sel = la_data_in[127:125];
// WB MI A
assign valid = wbs_cyc_i && wbs_stb_i;
assign wstrb = wbs_sel_i & {4{wbs_we_i}};
assign wbs_dat_o = rdata;
assign wdata = wbs_dat_i;
// IO
assign io_out = nonce;
assign io_oeb = {(`MPRJ_IO_PADS-1){rst}};
// IRQ
assign irq = 3'b000; // Unused
// Assuming LA probes [31:0] (aka: la_write0) are for controlling the nonce register.
// * NOTE: These are used as a mask for the la_data_in[?:?]
assign la_write0 = ~la_oenb[31:0] & ~{BITS{valid}};
assign la_write1 = ~la_oenb[63:32] & ~{BITS{valid}};
assign la_write2 = ~la_oenb[95:64] & ~{BITS{valid}};
assign la_write3 = ~la_oenb[127:96] & ~{BITS{valid}};
// Assuming LA probes [97:96] are for controlling the clk & reset
assign clk = (~la_oenb[96]) ? la_data_in[96] : wb_clk_i;
assign rst = (~la_oenb[97]) ? la_data_in[97] : wb_rst_i;
// TODO more LA muxing
always @(la_data_in || la_data_out || la_oenb || la_sel || nonce || block_header || target) begin
case (la_sel)
2'b00 : la_data_out[95:0] <= {{(95-BITS){1'b0}}, nonce};
2'b01 : la_data_out[95:0] <= block_header[95:0];
2'b10 : la_data_out[95:0] <= block_header[191:96];
2'b11 : la_data_out[95:0] <= block_header[287:192];
default:
// should not happen
endcase
end
// TODO create state machine for reading block header and passing to miner
miner_ctrl #(
.BITS(BITS)
) miner_ctrl (
.clk(clk),
.rst(rst),
.valid(valid),
.wb_we(wbs_we_i),
.wdata(wbs_dat_i),
.la_write0(la_write0),
.la_write3(la_write3),
.la_input0(la_data_in[31:0]),
.la_input3(la_data_in[127:96]),
.ready(wbs_ack_o),
.rdata(rdata),
.block_header(block_header),
.target(target),
.nonce(nonce),
.idle(o_idle)
)
endmodule // btc_miner_top
// miner_ctrl
module miner_ctrl #(
parameter BITS 32
)(
input clk,
input rst,
input valid,
input wb_we,
input [BITS-1:0] wdata,
input [BITS-1:0] la_write0,
input [BITS-1:0] la_write3,
input [BITS-1:0] la_input0,
input [BITS-1:0] la_input3,
output ready,
output [BITS-1:0] rdata,
output block_header,
output target,
output nonce,
output idle
);
// enum logic [1:0] {WAIT_IN=2'b00, READ_IN=2'b01, COMPUTE=2'b10, CHECK=2'b11, WRITE_OUT=} state;
enum int unsigned {WAIT_IN=0, READ_IN=1, COMPUTE=2, INCR_NONCE=3, WRITE_OUT=4} state;
reg ready;
reg [BITS-1:0] rdata;
reg [BITS-1:0] wb_data_reg;
reg [BITS-1:0] nonce;
reg [BITS-1:0] encoded_target;
reg [255:0] target;
reg miner_rst;
logic [639:0] block_header;
logic [255:0] o_hash_val;
logic o_done_hash;
wire idle;
wire start;
assign idle = (state == WAIT_IN) ? 1'b1 : 1'b0;
assign start = la_write[2] ? la_input3[2] : 1'b0;
// need to count to 640/32 = 20 (decimal). Only to 19 b/c nonce is last 32-bits
int unsigned count;
always @(posedge clk) begin
if (rst) begin
ready <= 0;
rdata <= 0;
wb_data_reg <= 0;
nonce <= 0;
encoded_target <= 0;
target <= 0;
count <= 0;
miner_rst <= 1;
block_header <= 0;
state <= WAIT_IN;
end else begin
ready <= 1'b0;
// state machine for controlling miner and I/O
case (state)
WAIT_IN:
// TODO?
miner_rst <= 1;
if (start == 1) begin
state <= READ_IN;
end
READ_IN:
// TODO
miner_rst <= 1;
if (valid && !ready) begin
ready <= 1'b1;
if (wb_we) begin
// TODO? read WB data into block header
block_header[BITS*count + 31 : BITS*count] <= wdata;
if (count == 18) begin
// TODO pass encoded_target into decoder module
encoded_target <= wdata;
target <= {{(255-BITS){1'b0}}, wdata};
count <= count + 1;
end else if (count >= 19) begin
block_header[639:608] <= nonce;
count <= 0;
nonce <= nonce + 1;
miner_rst <= 0;
state <= COMPUTE;
end else begin
count <= count + 1;
end
end
end
COMPUTE:
// start miner
if (o_done_hash) begin
// TODO target
if (o_hash_val < target) begin
state <= WRITE_OUT
end else begin
miner_rst <= 1;
block_header[639:608] <= nonce;
state <= INCR_NONCE;
end
end else begin
miner_rst <= 0;
end
INCR_NONCE:
// TODO?
miner_rst <= 0;
nonce <= nonce + 1;
state <= COMPUTE;
WRITE_OUT:
// TODO
if (valid && !ready) begin
ready <= 1'b1;
if (wb_we) begin
// WB should not be writing to user project
end else begin
// Place output hash on wishbone
rdata <= o_hash_val[BITS*count +:BITS];
if (count >= 7) begin
count <= 0;
state <= WAIT_IN;
end else begin
count <= count + 1;
end
end
end
default:
// should not happen
endcase
end
end
miner double_sha256_miner(
.block(block_header),
.clk(clk),
.rst(rst),
.hashed(o_hash_val),
.done(o_done_hash)
);
endmodule // miner_ctrl
// miner
module miner(
input logic [639:0] block,
input logic clk, rst,
output logic [255:0] hashed,
output logic done
);
logic [255:0] first_hash;
logic [255:0] secnd_hash;
logic second_run_rst;
logic done_first_hash;
logic done_secnd_hash;
sha_256 #(.MSG_SIZE(640), .PADDED_SIZE(1024)) first (.message(block), .hashed(first_hash), .clk(clk), .rst(rst), .done(done_first_hash));
sha_256 #(.MSG_SIZE(256), .PADDED_SIZE(512)) second (.message(first_hash), .hashed(secnd_hash), .clk(clk), .rst(second_run_rst), .done(done_secnd_hash));
// always @* second_run_rst <= 1'b0;
always @(posedge clk) begin
if (done_first_hash === 1'bX) second_run_rst <= 1'b1;
else if (done_first_hash == 1'b1) second_run_rst <= 1'b0;
end
assign done = done_secnd_hash;
assign hashed = {secnd_hash[7:0], secnd_hash[15:8], secnd_hash[23:16], secnd_hash[31:24], secnd_hash[39:32], secnd_hash[47:40], secnd_hash[55:48], secnd_hash[63:56], secnd_hash[71:64], secnd_hash[79:72], secnd_hash[87:80], secnd_hash[95:88], secnd_hash[103:96], secnd_hash[111:104], secnd_hash[119:112], secnd_hash[127:120], secnd_hash[135:128], secnd_hash[143:136], secnd_hash[151:144], secnd_hash[159:152], secnd_hash[167:160], secnd_hash[175:168], secnd_hash[183:176], secnd_hash[191:184], secnd_hash[199:192], secnd_hash[207:200], secnd_hash[215:208], secnd_hash[223:216], secnd_hash[231:224], secnd_hash[239:232], secnd_hash[247:240], secnd_hash[255:248]};
endmodule // miner
// sha_256
module sha_256 #(
parameter MSG_SIZE = 24,
parameter PADDED_SIZE = 512
)(
input logic [MSG_SIZE-1:0] message,
input logic clk, rst,
output logic [255:0] hashed,
output logic done
);
logic[PADDED_SIZE-1:0] padded;
sha_padder #(.MSG_SIZE(MSG_SIZE), .PADDED_SIZE(PADDED_SIZE)) padder (.message(message), .padded(padded));
sha_mainloop #(.PADDED_SIZE(PADDED_SIZE)) loop (.padded(padded), .hashed(hashed), .clk(clk), .rst(rst), .done(done));
endmodule // sha_256
// sha_padder
`define PACK_ARRAY(PK_WIDTH,PK_LEN,PK_SRC,PK_DEST) genvar pk_idx; generate for (pk_idx=0; pk_idx<(PK_LEN); pk_idx=pk_idx+1) begin; assign PK_DEST[((PK_WIDTH)*pk_idx+((PK_WIDTH)-1)):((PK_WIDTH)*pk_idx)] = PK_SRC[pk_idx][((PK_WIDTH)-1):0]; end; endgenerate
`define UNPACK_ARRAY(PK_WIDTH,PK_LEN,PK_DEST,PK_SRC) genvar unpk_idx; generate for (unpk_idx=0; unpk_idx<(PK_LEN); unpk_idx=unpk_idx+1) begin; assign PK_DEST[unpk_idx][((PK_WIDTH)-1):0] = PK_SRC[((PK_WIDTH)*unpk_idx+(PK_WIDTH-1)):((PK_WIDTH)*unpk_idx)]; end; endgenerate
module sha_padder #(
parameter MSG_SIZE = 24, // size of full message
parameter PADDED_SIZE = 512
)(
input logic [MSG_SIZE-1:0] message,
output logic [PADDED_SIZE-1:0] padded
);
localparam zero_width = PADDED_SIZE-MSG_SIZE-1-64;
localparam back_0_width = 64-$bits(MSG_SIZE);
assign padded = {message, 1'b1, {zero_width{1'b0}}, {back_0_width{1'b0}}, MSG_SIZE};
endmodule //sha_padder
// sha_mainloop
module sha_mainloop #(
parameter PADDED_SIZE = 512
)(
input logic [PADDED_SIZE-1:0] padded,
input logic clk, rst,
output logic [255:0] hashed,
output logic done
);
function [31:0] K;
input [6:0] x;
K = k[2047-x*32 -: 32];
endfunction
function automatic [31:0] W;
input [6:0] x;
input [6:0] y;
if(^x === 1'bX) W = 32'h777;
else W = (x<16) ? padded[((PADDED_SIZE-1-y*512)-x*32) -: 32] : rho1(W(x-2, y)) + W(x-7, y) + rho0(W(x-15, y)) + W(x-16, y);
endfunction
function automatic [31:0] rho0;
input [31:0] x;
if(^x === 1'bX) rho0 = 32'h888;
else rho0 = {x[6:0],x[31:7]} ^ {x[17:0],x[31:18]} ^ (x >> 3);
endfunction
function automatic [31:0] rho1;
input [31:0] x;
if(^x === 1'bX) rho1 = 32'h888;
else rho1 = {x[16:0],x[31:17]} ^ {x[18:0],x[31:19]} ^ (x >> 10);
endfunction
function [31:0] ch;
input [31:0] x,y,z;
if(^x === 1'bX) ch = 32'h888;
else ch = (x & y) ^ (~x & z);
endfunction
function [31:0] maj;
input [31:0] x,y,z;
if(^x === 1'bX) maj = 32'h888;
else maj = (x & y) ^ (x & z) ^ (y & z);
endfunction
function [31:0] sum0;
input [31:0] x;
if(^x === 1'bX) sum0 = 32'h888;
else sum0 = {x[1:0],x[31:2]} ^ {x[12:0],x[31:13]} ^ {x[21:0],x[31:22]};
endfunction
function [31:0] sum1;
input [31:0] x;
if(^x === 1'bX) sum1 = 32'h888;
else sum1 = {x[5:0],x[31:6]} ^ {x[10:0],x[31:11]} ^ {x[24:0],x[31:25]};
endfunction
logic [255:0] initial_hashes = {32'h6a09e667, 32'hbb67ae85, 32'h3c6ef372, 32'ha54ff53a, 32'h510e527f, 32'h9b05688c, 32'h1f83d9ab, 32'h5be0cd19};
logic [2047:0] k = {32'h428a2f98, 32'h71374491, 32'hb5c0fbcf, 32'he9b5dba5, 32'h3956c25b, 32'h59f111f1, 32'h923f82a4, 32'hab1c5ed5, 32'hd807aa98, 32'h12835b01, 32'h243185be, 32'h550c7dc3, 32'h72be5d74, 32'h80deb1fe, 32'h9bdc06a7, 32'hc19bf174, 32'he49b69c1, 32'hefbe4786, 32'h0fc19dc6, 32'h240ca1cc, 32'h2de92c6f, 32'h4a7484aa, 32'h5cb0a9dc, 32'h76f988da, 32'h983e5152, 32'ha831c66d, 32'hb00327c8, 32'hbf597fc7, 32'hc6e00bf3, 32'hd5a79147, 32'h06ca6351, 32'h14292967, 32'h27b70a85, 32'h2e1b2138, 32'h4d2c6dfc, 32'h53380d13, 32'h650a7354, 32'h766a0abb, 32'h81c2c92e, 32'h92722c85, 32'ha2bfe8a1, 32'ha81a664b, 32'hc24b8b70, 32'hc76c51a3, 32'hd192e819, 32'hd6990624, 32'hf40e3585, 32'h106aa070, 32'h19a4c116, 32'h1e376c08, 32'h2748774c, 32'h34b0bcb5, 32'h391c0cb3, 32'h4ed8aa4a, 32'h5b9cca4f, 32'h682e6ff3, 32'h748f82ee, 32'h78a5636f, 32'h84c87814, 32'h8cc70208, 32'h90befffa, 32'ha4506ceb, 32'hbef9a3f7, 32'hc67178f2};
logic [31:0] a, b, c, d, e, f, g, h, t1, t2;
logic [31:0] h1, h2, h3, h4, h5, h6, h7, h8;
logic [6:0] j;
logic [6:0] i;
localparam N = PADDED_SIZE/512; // number of blocks
logic [31:0] ch_efg, maj_abc, sum0_a, sum1_e, kj, wj;
always_comb begin
ch_efg = ch(e,f,g);
maj_abc = maj(a,b,c);
sum0_a = sum0(a);
sum1_e = sum1(e);
wj = W(j, i);
kj = K(j);
end
always @(negedge clk) begin
// t1 <= h + sum1(e) + ch(e,f,g) + K(j) + W(j);
// t2 <= sum0(a) + maj(a,b,c);
t1 <= (h + sum1_e + ch_efg + kj + wj)%4294967296;
t2 <= (sum0_a + maj_abc)%4294967296;
end
always @(posedge clk or posedge rst) begin
if(rst) begin
i <= 1'b0;
j <= 1'bX;
h1 <= 32'h6a09e667;
h2 <= 32'hbb67ae85;
h3 <= 32'h3c6ef372;
h4 <= 32'ha54ff53a;
h5 <= 32'h510e527f;
h6 <= 32'h9b05688c;
h7 <= 32'h1f83d9ab;
h8 <= 32'h5be0cd19;
end
else if (^j === 1'bX && ^i !== 1'bX) begin
a <= h1;
b <= h2;
c <= h3;
d <= h4;
e <= h5;
f <= h6;
g <= h7;
h <= h8;
j <= 1'd0;
end
else if (j < 64) begin
h <= g;
g <= f;
f <= e;
e <= (d+t1)%4294967296;
d <= c;
c <= b;
b <= a;
a <= (t1+t2)%4294967296;
j <= j+1;
end
else if (j == 64) begin
h1 <= a + h1;
h2 <= b + h2;
h3 <= c + h3;
h4 <= d + h4;
h5 <= e + h5;
h6 <= f + h6;
h7 <= g + h7;
h8 <= h + h8;
j <= 1'bX;
if (i<N-1) i <= i+1;
else begin
i <= 1'bX;
done <= 1'b1;
end
end
end
assign hashed = {h1, h2, h3, h4, h5, h6, h7, h8};
endmodule // sha_mainloop
`default_nettype wire