├── .gitignore ├── .gitmodules ├── LICENSE.txt ├── README.md ├── docs └── README.md ├── helpers ├── 53-lattice-ftdi.rules ├── generate_target_vh.py ├── midstate │ ├── Makefile │ ├── midstate.py │ ├── midstate_sha256.c │ └── midstate_test.py ├── setup-dev-environment.sh └── verify-result │ ├── example.toml │ └── verify.py └── src ├── Makefile ├── SHA256_K.v ├── external_io.v ├── pinout-ice40hx8k-b-evn.pcf ├── pinout-icepool-ice40hx8k.pcf ├── pinout-icepool-ice40up5k.pcf ├── sha_round.v ├── sha_unit.v ├── shapool.v ├── targets ├── target-1.vh ├── target-1024.vh ├── target-128.vh ├── target-16.vh ├── target-2.vh ├── target-2048.vh ├── target-256.vh ├── target-32.vh ├── target-4.vh ├── target-4096.vh ├── target-512.vh ├── target-64.vh ├── target-8.vh └── target-8192.vh ├── tests ├── Makefile ├── btc_four_zeroes │ ├── Makefile │ ├── main.c │ └── rtl │ │ ├── Makefile │ │ └── top_test.v ├── btc_sixteen_zeroes │ ├── Makefile │ ├── main.c │ └── rtl │ │ ├── Makefile │ │ └── top_test.v ├── external_io_usage_tb.v ├── ram_init_test │ ├── Makefile │ ├── main.c │ └── rtl │ │ ├── Makefile │ │ └── top_test.v ├── sha_round_usage_tb.v ├── sha_unit_btc_tb.v ├── sha_unit_usage_tb.v ├── shapool_usage_tb.v └── top_usage_tb.v ├── top.v ├── top_hx8k.v ├── top_up5k.v └── w_expand.v /.gitignore: -------------------------------------------------------------------------------- 1 | # Gateware build artifacts 2 | *.out 3 | *.blif 4 | *.json 5 | *.asc 6 | *.bin 7 | 8 | # iverilog artifacts 9 | obj_dir/ 10 | 11 | # Test bench artifacts 12 | *.vcd 13 | 14 | # Hardware test artifacts 15 | *.o 16 | *.a 17 | 18 | # Python 19 | dist/ 20 | build/ 21 | __pycache__/ 22 | .pytest_cache/ 23 | *.pyc 24 | *.egg-info/ -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/tests/btc_four_zeroes/icepool-driver"] 2 | path = src/tests/vendor/icepool-driver 3 | url = https://github.com/jkiv/icepool-driver 4 | [submodule "src/tests/vendor/munit"] 5 | path = src/tests/vendor/munit 6 | url = https://github.com/nemequ/munit 7 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2018 Jon Kivinen (jkiv) 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # shapool-core 2 | 3 | `shapool-core` is an FPGA core for SHA256d mining (e.g. BTC) for use with 4 | Lattice Semiconductor [iCE40](https://www.latticesemi.com/iCE40) devices using the open-source [Project IceStorm](http://www.clifford.at/icestorm/) 5 | toolchain. 6 | 7 | The purpose of this project is to evaluate the iCE40 devices for mining and 8 | produce a hobbyist/DIY low power FPGA SHA256d miner. 9 | 10 | This project aims to support multiple targets including 11 | 12 | * [ICE40HX8K-B-EVN](http://www.latticesemi.com/en/Products/DevelopmentBoardsAndKits/iCE40HX8KBreakoutBoard.aspx) 13 | * [jkiv/icepool-board](https://github.com/jkiv/icepool-board) 14 | * ... and others. 15 | 16 | (Support for [ECP5](https://www.latticesemi.com/Products/FPGAandCPLD/ECP5) boards using the [Project Trellis](https://github.com/YosysHQ/prjtrellis) open-source toolchain is [planned](https://github.com/jkiv/shapool-core/issues/5) as well.) 17 | 18 | ### Support 19 | 20 | Please consider supporting this project and others like it by donating: 21 | 22 | * ☕: [ko-fi.com/jkiv_](https://ko-fi.com/jkiv_) 23 | * ₿: `13zRrs1YDdooUN5WtfXRSDn8KnJdok4qG9` 24 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | ## About `shapool-core` 2 | 3 | `shapool-core` is an FPGA core that computes double-SHA256 hashes for the purposes of cryptocurrency mining, e.g. Bitcoin. 4 | 5 | `shapool-core` is intended to be used in a cluster-like environment but should work as a stand-alone core, i.e. a cluster of one. 6 | 7 | Each `shapool-core` device in a cluster is given the same job data by the host device. Each device works on the job until it either finishes or is interrupted by the host device. 8 | 9 | The work required for each job can be divided among the cluster by providing each `shapool-core` device with unique parameters such that no two devices will perform the same work. 10 | 11 | ## Signal Descriptions 12 | 13 | ``` 14 | shapool-core 15 | +---------------+ 16 | clk ---|> |--> ready_n 17 | | | 18 | reset_n -->| |--> status_led_n 19 | | | 20 | sck0 -->| | 21 | sdi0 -->| SPI 0 | 22 | cs0_n -->| | 23 | | | 24 | sck1 -->| | 25 | sdi1 -->| SPI 1 |--> sdo1 26 | cs1_n -->| | 27 | +---------------+ 28 | ``` 29 | 30 | * `clk` - external core clock input 31 | * `reset_n` - external asynchronous reset input 32 | * `sck0` - serial data clock input (SPI interface 0) 33 | * `sdi0` - serial data input (SPI interface 0) 34 | * `cs0_n` - active-low chip-select input (SPI interface 0) 35 | * `sck1` - serial data clock input (SPI interface 1) 36 | * `sdi1` - serial data input (SPI interface 1) 37 | * `sdo1` - serial data output (SPI interface 1) 38 | * `cs1_n` - active-low chip-select input (SPI interface 1) 39 | * `ready_n` - active-low, open-drain "data ready" output 40 | * `status_led_n` - active-low indicator LED output 41 | 42 | SPI interface 0 is a one-to-many interface which allows the host device load the same job data to all `shapool-core` devices on the SPI bus. Note that this interface does not have a data output. 43 | 44 | SPI interface 1 is a daisy-chained interface which allows the host device to load each `shapool-core` with device-specific data, as well as read device-specific results back. 45 | 46 | Both SPI interfaces are SPI mode 0, 0 and most-significant-bit-first. 47 | 48 | ## Behavioural Description 49 | 50 | `shapool-core` has four main states: 51 | 52 | 1. `RESET` - Reset 53 | 2. `LOAD` - Load device and job data 54 | 3. `EXEC` - Execute job 55 | 4. `DONE` - Done executing, read result 56 | 57 | The device enters the `RESET` state whenever the device's `reset_n` signal is asserted. This state resets execution state but retains job and configuration data. 58 | 59 | Immediately after resetting, the device enters the `LOAD` state, while `reset_n` is still asserted. During the `LOAD` state, both SPI interfaces 0 and 1 are active. Job data and device configuration data can be written to SPI interfaces 0 and 1 respectively. 60 | 61 | After the device is loaded with its job and configuration data, `reset_n` can be de-asserted to put the device into the `EXEC` state. 62 | 63 | The device will work on the provided job until it is done. If and when the device is done its job, the device will enter the `DONE` state. 64 | 65 | Each device who successfully completes its job will assert its `ready_n` signal. The `ready_n` signal tells the host device that work is completed and the result can be read on SPI interface 1. If the host asserts `cs1_n` while devices are in the `EXEC` state, this will force all devices to enter the `DONE` state. 66 | 67 | Once all devices are in the `DONE` state, SPI interface 1 is available to read from. Devices who successfully finished their job will provide the result of their job. Otherwise, the device will provide a result of all zeros. 68 | 69 | After reading the results from the devices, the host can repeat the process by asserting `reset_n` and loading a new job on SPI interface 0. 70 | 71 | ## Device configurations 72 | 73 | Device-specific configuration data can be written to the device using SPI interface 1 while `reset_n` is asserted. 74 | 75 | This typically only needs to be done when the device is initialized, e.g. after a power cycle or reprogramming. 76 | 77 | 78 | 79 | * `nonce start (MSB)`: the most-significant byte of the initial value for the nonce (0-255). 80 | 81 | ## Job configuration 82 | 83 | Job configuration data can be written to the device using SPI interface 0 while `reset_n` is asserted. 84 | 85 | This is done as often as required, e.g. after completed or expired jobs. 86 | 87 | 88 | 89 | * `SHA256 state`: the internal state of the first SHA256 after the first block is hashed but before the second block is hashed. 90 | * `message head`: the start of the second block of the first hash. 91 | 92 | ## Job results 93 | 94 | Job result data can be read using SPI interface 1 when `cs1_n` is asserted, `reset_n` is de-asserted, and all devices have entered the `DONE` state. 95 | 96 | Devices who did not finish their work will provide all zeros as a result. 97 | 98 | 99 | 100 | * `match flags`: bit positions having `1` denote pipelines (cores) that generated a winning hash. 101 | * `winning nonce`: the nonce that caused the winning hash (uncorrected), or zero if no winning nonce found. 102 | 103 | The `winning nonce` provided by devices is "uncorrected." This means that the host is responsible for reconstructing the actual nonce from `match flags` and other knowledge about the devices. 104 | 105 | 1. The nonce value is +2 greater than the actual nonce that caused the match. 106 | 2. Any hard-coded most-significant-bits are zeroed (e.g. in a multi-core configuration.) 107 | 3. `nonce_start_MSB` has not been applied to the returned value. 108 | 109 | So, reconstructing the nonce can be done as follows: 110 | 111 | 1. Subtract 2 from the nonce. 112 | 113 | 2. Correct the most-significant-bits using the value of match flags and the number of hard-coded bits per device. 114 | 115 | For example, if there are 8 cores per device then there are `ceil(log2(8)) = 3` hard-coded bits per core. If match flags is `0x04` (bit position 2), then the most-significant-bits are `b010 = 2**2`. 116 | 117 | 3. Apply `nonce_start_MSB` corresponding to the device that supplied the nonce. 118 | 119 | For example, if `nonce_start_MSB = 0x80` and there are 3 hard-coded bits per core, then XOR the result with `0x80 << (24-3) = 0x01000000` -------------------------------------------------------------------------------- /helpers/53-lattice-ftdi.rules: -------------------------------------------------------------------------------- 1 | # ICE40HX8K-EVB Development Board (shapool-core) 2 | #ACTION=="add", ATTR{idVendor}=="0403", ATTR{idProduct}=="6010", MODE:="666" # 2018 3 | ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6010", MODE="0660", GROUP="plugdev", TAG+="uaccess" 4 | -------------------------------------------------------------------------------- /helpers/generate_target_vh.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | 5 | if __name__ == '__main__': 6 | namebase = 'target' 7 | for n in range(0, 14): 8 | difficulty = 2**n 9 | target = 32+n 10 | filename = f'{namebase}-{difficulty}.vh' 11 | 12 | with open(filename, 'w') as f: 13 | print(f'Writing \'{filename}\'', file=sys.stderr) 14 | f.writelines(f'`define TARGET {target}\n') 15 | 16 | print('Done!', file=sys.stderr) 17 | -------------------------------------------------------------------------------- /helpers/midstate/Makefile: -------------------------------------------------------------------------------- 1 | .phony : all 2 | all : midstate_sha256.so 3 | 4 | midstate_sha256.so : midstate_sha256.c 5 | gcc --std=c99 -Wall -fPIC -shared -o $@ $^ 6 | 7 | .phony : test 8 | test : midstate_sha256.so 9 | pytest 10 | 11 | .phony : clean 12 | clean : 13 | rm -f midstate_sha256.so 14 | rm -rf __pycache__/ 15 | rm -rf .pytest_cache/ -------------------------------------------------------------------------------- /helpers/midstate/midstate.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import binascii 3 | import ctypes 4 | from os import path 5 | import sys 6 | 7 | _midstate = ctypes.CDLL(path.abspath(path.join(path.dirname(__file__), "midstate_sha256.so"))) 8 | 9 | def _ffi(ffi, function_name, return_type, arg_types): 10 | fn = ffi.__getattr__(function_name) 11 | fn.restype = return_type 12 | fn.argtypes = arg_types 13 | return fn 14 | 15 | class ShaState: 16 | def __init__(self): 17 | self._state = ctypes.create_string_buffer(32) 18 | _init_state = _ffi(_midstate, 'init_state', None, [ctypes.c_char_p,]) 19 | _init_state(self._state) 20 | 21 | def __del__(self): 22 | del self._state 23 | 24 | def update(self, block): 25 | _update_state = _ffi(_midstate, 'update_state', None, [ctypes.c_char_p, ctypes.c_char_p,]) 26 | _update_state(self._state, block) 27 | 28 | def as_b64(self, byte_swap=False): 29 | return binascii.b2a_base64(self.as_bin(byte_swap)).decode('utf-8') 30 | 31 | def as_hex(self, byte_swap=False): 32 | return binascii.b2a_hex(self.as_bin(byte_swap)).decode('utf-8') 33 | 34 | def as_bin(self, byte_swap=False): 35 | result = bytes(self._state) 36 | if byte_swap: 37 | return result[3::-1] + result[7:3:-1] + \ 38 | result[11:7:-1] + result[15:11:-1] + \ 39 | result[19:15:-1] + result[23:19:-1] + \ 40 | result[27:23:-1] + result[31:27:-1] 41 | else: 42 | return result 43 | 44 | def stream_blocks(stream, block_size=64): 45 | while True: 46 | block_data = stream.read(block_size) 47 | 48 | if not block_data or block_size != len(block_data): 49 | return 50 | 51 | yield block_data 52 | 53 | def get_midstate(stream, last_block=None): 54 | 55 | state = ShaState() 56 | 57 | for n, block_data in enumerate(stream_blocks(stream)): 58 | state.update(block_data) 59 | 60 | if n == last_block: 61 | return state 62 | 63 | if last_block: 64 | raise RuntimeError(f'Stream closed before reaching block {last_block}. Last block was {n}.') 65 | 66 | return state 67 | 68 | if __name__ == '__main__': 69 | 70 | parser = argparse.ArgumentParser(description='Compute intermediate states of a SHA256 hash.') 71 | 72 | parser.add_argument('-i', '--input-file', type=str, help='input file (default: stdin)', default=None) 73 | parser.add_argument('-o', '--output-file', type=str, help='output file (default: stdout)', default=None) 74 | parser.add_argument('-b', '--block', type=int, help='return the state after specified block number (zero-indexed, default: last full block).') 75 | 76 | output_format = parser.add_mutually_exclusive_group(required=False) 77 | output_format.add_argument('--hex', help='Output as hexadecimal.', action='store_true', default=True) 78 | output_format.add_argument('--base64', help='Output as Base64.', action='store_true', default=False) 79 | output_format.add_argument('--bin', help='Output as binary.', action='store_true', default=False) 80 | 81 | args = parser.parse_args() 82 | 83 | if args.block and args.block < 0: 84 | raise ValueError("Block number must be >= 0.") 85 | 86 | if args.input_file is None: 87 | state = get_midstate(sys.stdin.buffer, args.block) 88 | else: 89 | with open(args.input_file, 'rb') as s: 90 | state = get_midstate(s, args.block) 91 | 92 | if args.output_file is None: 93 | s = sys.stdout 94 | 95 | if args.bin: 96 | s.buffer.write(state.as_bin()) 97 | elif args.base64: 98 | s.write(state.as_b64()) 99 | elif args.hex: 100 | s.write(state.as_hex() + u'\n') 101 | 102 | else: 103 | with open(args.output_file, 'wb') as s: 104 | if args.bin: 105 | s.write(state.as_bin()) 106 | elif args.base64: 107 | s.write(state.as_b64().rstrip(u'\n')) 108 | elif args.hex: 109 | s.write(state.as_hex()) 110 | -------------------------------------------------------------------------------- /helpers/midstate/midstate_sha256.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static const uint32_t SHA256_K[64] = { 6 | 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 7 | 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 8 | 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 9 | 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 10 | 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 11 | 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 12 | 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 13 | 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 14 | }; 15 | 16 | static const uint32_t SHA256_H0[8] = { 17 | 0x6a09e667, 0xbb67ae85, 18 | 0x3c6ef372, 0xa54ff53a, 19 | 0x510e527f, 0x9b05688c, 20 | 0x1f83d9ab, 0x5be0cd19 21 | }; 22 | 23 | static uint32_t ror32(uint32_t x, uint32_t b) { 24 | return (x>>b)|(x<<(32-b)); 25 | } 26 | 27 | static uint32_t maj(uint32_t x, uint32_t y, uint32_t z) { 28 | //return (x & y) ^ (x & z) ^ (y & z); 29 | return ((y & z) | (x & (y | z))); 30 | } 31 | 32 | static uint32_t ch(uint32_t x, uint32_t y, uint32_t z) { 33 | //return (x & y) ^ ((~x) & z); 34 | return z ^ (x & (z ^ y)); 35 | } 36 | 37 | static uint32_t bsig0(uint32_t x) { 38 | return ror32(x,2) ^ ror32(x,13) ^ ror32(x,22); 39 | } 40 | 41 | static uint32_t bsig1(uint32_t x) { 42 | return ror32(x,6) ^ ror32(x,11) ^ ror32(x,25); 43 | } 44 | 45 | static uint32_t ssig0(uint32_t x) { 46 | return ror32(x,7) ^ ror32(x,18) ^ (x>>3); 47 | } 48 | 49 | static uint32_t ssig1(uint32_t x) { 50 | return ror32(x,17) ^ ror32(x,19) ^ (x>>10); 51 | } 52 | 53 | static uint32_t w_expand(uint32_t w2, uint32_t w7, uint32_t w15, uint32_t w16) { 54 | return ssig1(w2) + w7 + ssig0(w15) + w16; 55 | } 56 | 57 | static uint32_t byte_swap_32(uint32_t x) 58 | { 59 | return ((x & 0x000000ff) << 24) | 60 | ((x & 0x0000ff00) << 8) | 61 | ((x & 0x00ff0000) >> 8) | 62 | ((x & 0xff000000) >> 24); 63 | } 64 | 65 | static void sha256_update(uint32_t* H, const uint32_t* m) { 66 | uint32_t w[64] = {0}; // message schedule 67 | 68 | uint32_t a,b,c,d,e,f,g,h = 0; // working variables 69 | uint32_t t1,t2 = 0; // temporary values 70 | 71 | // Initialize working variables 72 | a = H[0]; 73 | b = H[1]; 74 | c = H[2]; 75 | d = H[3]; 76 | e = H[4]; 77 | f = H[5]; 78 | g = H[6]; 79 | h = H[7]; 80 | 81 | // Mangle a-h using m 82 | for (uint8_t t = 0; t < 64; t++) { 83 | // Compute w[t] 84 | if (t < 16) { 85 | // 32-bit input is considered big-endian by spec 86 | // whereas x86 is little endian. Swap byte order. 87 | w[t] = byte_swap_32(m[t]); 88 | } 89 | else { 90 | w[t] = w_expand(w[t-2], w[t-7], w[t-15], w[t-16]); 91 | } 92 | 93 | // Compute t1, t2 94 | t1 = h; 95 | t1 += bsig1(e); 96 | t1 += ch(e,f,g); 97 | t1 += SHA256_K[t]; 98 | t1 += w[t]; 99 | 100 | t2 = bsig0(a); 101 | t2 += maj(a,b,c); 102 | 103 | // Update working variables (order here matters) 104 | h = g; 105 | g = f; 106 | f = e; 107 | e = d+t1; 108 | d = c; 109 | c = b; 110 | b = a; 111 | a = t1+t2; 112 | } 113 | 114 | // Update H 115 | H[0] += a; 116 | H[1] += b; 117 | H[2] += c; 118 | H[3] += d; 119 | H[4] += e; 120 | H[5] += f; 121 | H[6] += g; 122 | H[7] += h; 123 | } 124 | 125 | void init_state(uint32_t* state) { 126 | for (size_t i = 0; i < 8; i++) { 127 | state[i] = SHA256_H0[i]; 128 | } 129 | } 130 | 131 | void update_state(uint32_t* state, const uint32_t* block) { 132 | sha256_update(state, block); 133 | } -------------------------------------------------------------------------------- /helpers/midstate/midstate_test.py: -------------------------------------------------------------------------------- 1 | import binascii 2 | import midstate 3 | import pytest 4 | import struct 5 | 6 | def test_initial_state(): 7 | SHA256_H0 = bytes([ 8 | # Byte-swap 32-bit words 9 | 0x6a, 0x09, 0xe6, 0x67, 10 | 0xbb, 0x67, 0xae, 0x85, 11 | 0x3c, 0x6e, 0xf3, 0x72, 12 | 0xa5, 0x4f, 0xf5, 0x3a, 13 | 0x51, 0x0e, 0x52, 0x7f, 14 | 0x9b, 0x05, 0x68, 0x8c, 15 | 0x1f, 0x83, 0xd9, 0xab, 16 | 0x5b, 0xe0, 0xcd, 0x19 17 | ]) 18 | 19 | s = midstate.ShaState() 20 | 21 | assert s.as_bin(True) == SHA256_H0 22 | 23 | def test_btc_four_zeroes(): 24 | expected_state = bytes([ 25 | 0xdc, 0x6a, 0x3b, 0x8d, 0x0c, 0x69, 0x42, 0x1a, 26 | 0xcb, 0x1a, 0x54, 0x34, 0xe5, 0x36, 0xf7, 0xd5, 27 | 0xc3, 0xc1, 0xb9, 0xe4, 0x4c, 0xbb, 0x9b, 0x8f, 28 | 0x95, 0xf0, 0x17, 0x2e, 0xfc, 0x48, 0xd2, 0xdf, 29 | ]) 30 | 31 | expected_second_block = bytes([ 32 | 0xdc, 0x14, 0x17, 0x87, 33 | 0x35, 0x8b, 0x05, 0x53, 34 | 0x53, 0x5f, 0x01, 0x19 35 | ]) 36 | 37 | message = struct.pack(" str: 11 | # https://en.bitcoin.it/wiki/Difficulty 12 | exp = bits >> 24 13 | mant = bits & 0xffffff 14 | target_hexstr = '%064x' % (mant * (1<<(8*(exp - 3)))) 15 | target_str = target_hexstr.decode('hex') 16 | 17 | @functools.lru_cache 18 | def get_difficulty_bm(size: int) -> bytes: 19 | assert(size >= 0 and size <= 32*8) 20 | 21 | bin_mask = ('1' * size) + ('0' * (32*8-size)) 22 | 23 | return bytes(int(bin_mask[i:i+8], 2) for i in range(0, 32*8, 8)) 24 | 25 | 26 | def make_header(version: int, previous_block: bytes, merkel_root: bytes, time: int, bits: int) -> bytes: 27 | return struct.pack(" dict: 33 | version = slice(0,4) 34 | previous_block = slice(4,36) 35 | merkel_root = slice(36,68) 36 | time = slice(68,72) 37 | bits = slice(72,76) 38 | nonce = slice(76,80) 39 | 40 | return { 41 | "version": header[version], 42 | "previous_block": header[previous_block], 43 | "merkel_root": header[merkel_root], 44 | "time": header[time], 45 | "bits": header[bits], 46 | "nonce": header[nonce] if has_nonce else None 47 | } 48 | 49 | def test_hash_leading_zeroes(hash: bytes, target_zeroes: int = 64) -> bool: 50 | zero_mask = get_difficulty_bm(target_zeroes) 51 | return all(b & byte_mask == 0 for b, byte_mask in zip(hash, zero_mask)) 52 | 53 | def compute_hash(header: bytes) -> bytes: 54 | return hashlib.sha256(hashlib.sha256(header).digest()).digest() 55 | 56 | def compute_nonce(header: bytes, leading_zeroes: int = 64, nonce_gen: Iterator = None) -> int: 57 | 58 | assert(len(header) == 76) 59 | 60 | if nonce_gen is None: 61 | nonce_gen = range(2**32) 62 | 63 | for nonce in nonce_gen: 64 | hash = compute_hash(header + struct.pack(" bytes: 72 | assert(len(header) == 76) 73 | 74 | header = header + struct.pack("] --> compute nonce 86 | # verify [-f ] --> check nonce 87 | # verify [-f ] --> print case in blocks 88 | # verify [-f ] -- 89 | 90 | EXAMPLE_TOML = '''# Example TOML 91 | version = 2 92 | previous_block = "000000000000000117c80378b8da0e33559b5997f2ad55e2f7d18ec1975b9717" 93 | merkel_root = "871714dcbae6c8193a2bb9b2a69fe1c0440399f38d94b3a0f1b447275a29978a" 94 | time = 0x53058b35 95 | bits = 0x19015f53 96 | ''' 97 | 98 | if __name__ == "__main__": 99 | parser = argparse.ArgumentParser(description="") 100 | 101 | parser.add_argument("difficulty", type=int, help="number of leading zeroes to check hash against") 102 | parser.add_argument("-c", "--case", type=str, required=False, help="path to toml file containing case data (default: stdin)") 103 | 104 | group = parser.add_mutually_exclusive_group() 105 | group.add_argument("-n", "--nonce", type=int, required=False, help="nonce to verify") 106 | group.add_argument("-r", "--range", type=str, required=False, help="set of nonce values to check (comma-separated values or start:end)") 107 | 108 | args = parser.parse_args() 109 | 110 | # Load case 111 | case = None 112 | 113 | if args.case and args.case != '-': 114 | case = toml.load(args.case) 115 | else: 116 | case = toml.load(sys.stdin) 117 | 118 | # TODO validate `case` 119 | 120 | case["previous_block"] = binascii.a2b_hex(case["previous_block"])[::-1] 121 | case["merkel_root"] = binascii.a2b_hex(case["merkel_root"])[::-1] 122 | 123 | header = make_header(**case) 124 | 125 | # Run case 126 | if args.nonce: 127 | # Verify nonce 128 | valid = verify_nonce(header, args.nonce, args.difficulty) 129 | 130 | if not valid: 131 | print(f"Invalid nonce: {args.nonce}", file=sys.stderr) 132 | sys.exit(1) 133 | else: 134 | sys.exit(0) 135 | 136 | else: 137 | 138 | # Interpret nonce range 139 | nonce_gen = None 140 | if args.range: 141 | if ',' in args.range: 142 | nonce_gen = [int(n.strip()) for n in args.range.split(',')] 143 | elif ':' in args.range: 144 | start, end = (int(x) for x in args.range.split(':', 1)) 145 | nonce_gen = range(start, end) 146 | 147 | # Compute nonce 148 | nonce = compute_nonce(header, args.difficulty, nonce_gen) 149 | 150 | if nonce is None: 151 | print("No solution found.", file=sys.stderr) 152 | sys.exit(1) 153 | else: 154 | print(nonce) 155 | sys.exit(0) 156 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | DIFFICULTY=1 2 | 3 | # Build all bitstreams 4 | 5 | .phony: all 6 | 7 | all: shapool-ice40hx8k-b-evn.bin shapool-icepool-ice40hx8k.bin # shapool-icepool-ice40up5k.bin 8 | 9 | # Validate verilog code 10 | 11 | .phony: lint 12 | 13 | lint : top.v shapool.v sha_unit.v sha_round.v w_expand.v SHA256_K.v external_io.v 14 | verilator -Wall -cc top.v 15 | 16 | # Clean up 17 | 18 | .phony: clean 19 | clean : 20 | rm -f target.vh 21 | rm -f shapool-*.out \ 22 | shapool-*.json \ 23 | shapool-*.asc \ 24 | shapool-*.bin 25 | rm -rf ./obj_dir 26 | 27 | # Synthesize for device type 28 | 29 | shapool-ice40-hx8k.json : top_hx8k.v top.v shapool.v sha_unit.v sha_round.v w_expand.v SHA256_K.v external_io.v 30 | rm -f target.vh 31 | ln -s targets/target-$(DIFFICULTY).vh target.vh 32 | yosys -p 'synth_ice40 -top top_hx8k -json $@ -abc2 -retime' $^ 33 | 34 | shapool-ice40-up5k.json : top_up5k.v top.v shapool.v sha_unit.v sha_round.v w_expand.v SHA256_K.v external_io.v 35 | rm -f target.vh 36 | ln -s targets/target-$(DIFFICULTY).vh target.vh 37 | yosys -p 'synth_ice40 -top top_up5k -json $@ -abc2 -retime' $^ 38 | 39 | # Route for target board 40 | 41 | shapool-ice40hx8k-b-evn.asc : shapool-ice40-hx8k.json pinout-ice40hx8k-b-evn.pcf 42 | nextpnr-ice40 --package ct256 --hx8k --json shapool-ice40-hx8k.json --pcf pinout-ice40hx8k-b-evn.pcf --asc $@ 43 | 44 | #shapool-icepool-ice40up5k.asc : shapool-ice40-up5k.json pinout-ice40up5k-b-evn.pcf 45 | # nextpnr-ice40 --package sg48 --up5k --json shapool-ice40-up5k.json --pcf pinout-ice40up5k-b-evn.pcf --asc $@ 46 | 47 | shapool-icepool-ice40hx8k.asc : shapool-ice40-hx8k.json pinout-icepool-ice40hx8k.pcf 48 | nextpnr-ice40 --package bg121 --hx8k --json shapool-ice40-hx8k.json --pcf pinout-icepool-ice40hx8k.pcf --asc $@ 49 | 50 | # Generate bitstream for target board 51 | 52 | shapool-ice40hx8k-b-evn.bin : shapool-ice40hx8k-b-evn.asc 53 | icepack $^ $@ 54 | 55 | #shapool-icepool-ice40up5k.bin : shapool-icepool-ice40up5k.asc 56 | # icepack $^ $@ 57 | 58 | shapool-icepool-ice40hx8k.bin : shapool-icepool-ice40hx8k.asc 59 | icepack $^ $@ 60 | 61 | # Run timing simulations 62 | 63 | .phony: time-ice40hx8k-b-evn 64 | time-ice40hx8k-b-evn : shapool-ice40hx8k-v-evn.asc 65 | icetime -t -m -d hx8k -P ct256 -p pinout-ice40hx8k-b-evn.pcf -o - $^ 66 | 67 | #.phony: time-ice40up5k-b-evn 68 | #time-ice40up5k-b-evn : shapool-icepool-ice40up5k.asc 69 | # icetime -t -m -d up5k -P sg48 -p pinout-ice40up5k-b-evn.pcf -o - $^ 70 | 71 | .phony: time-icepool-ice40hx8k 72 | time-icepool-ice40hx8k : shapool-icepool-ice40hx8k.asc 73 | #icetime -t -m -d hx8k -P cm121 -p pinout-icepool-ice40hx8k.pcf -o - $^ 74 | icetime -t -m -d hx8k -P bg121 -p pinout-icepool-ice40hx8k.pcf -o - $^ 75 | 76 | # Make target binary and upload to device 77 | 78 | .phony: flash-ice40hx8k-b-evn 79 | flash-ice40hx8k-b-evn : shapool-ice40-hx8k-ct256.bin 80 | iceprog -v -S $^ 81 | 82 | #.phony: flash-ice40up5k-b-evn 83 | #flash-ice40up5k-b-evn : shapool-icepool-ice40up5k.bin 84 | # iceprog -v -S $^ 85 | 86 | .phony: flash-icepool-ice40hx8k 87 | flash-icepool-ice40hx8k : shapool-icepool-ice40hx8k.bin 88 | iceprog -v -S $^ 89 | -------------------------------------------------------------------------------- /src/SHA256_K.v: -------------------------------------------------------------------------------- 1 | /* 2 | * Two read-only 256x16 RAM banks for 32-bit word access per clock cycle, addressed by `round`. 3 | */ 4 | module SHA256_K ( 5 | clk, 6 | round, 7 | Kt 8 | ); 9 | 10 | input clk; 11 | input [5:0] round; 12 | 13 | `ifndef VERILATOR 14 | 15 | wire [15:0] data_hi; 16 | wire [15:0] data_lo; 17 | 18 | output wire [31:0] Kt; 19 | assign Kt = { data_hi, data_lo }; 20 | 21 | wire [7:0] address; 22 | assign address = { 2'b10, round }; 23 | 24 | SB_RAM40_4K ram256x16_hi ( 25 | .RDATA(data_hi), 26 | .RADDR(address), 27 | .RCLK(clk), 28 | .RCLKE(1), 29 | .RE(1), 30 | .WADDR(0), 31 | .WCLK(0), 32 | .WCLKE(0), 33 | .WDATA(0), 34 | .WE(0), 35 | .MASK(0) 36 | ); 37 | 38 | SB_RAM40_4K ram256x16_lo ( 39 | .RDATA(data_lo), 40 | .RADDR(address), 41 | .RCLK(clk), 42 | .RCLKE(1), 43 | .RE(1), 44 | .WADDR(0), 45 | .WCLK(0), 46 | .WCLKE(0), 47 | .WDATA(0), 48 | .WE(0), 49 | .MASK(0) 50 | ); 51 | 52 | defparam ram256x16_hi.READ_MODE = 0; // 0 = 256x16 53 | defparam ram256x16_hi.WRITE_MODE = 0; // 0 = 256x16 54 | 55 | defparam ram256x16_lo.READ_MODE = 0; // 0 = 256x16 56 | defparam ram256x16_lo.WRITE_MODE = 0; // 0 = 256x16 57 | 58 | defparam ram256x16_hi.INIT_0 = 0; 59 | defparam ram256x16_hi.INIT_1 = 0; 60 | defparam ram256x16_hi.INIT_2 = 0; 61 | defparam ram256x16_hi.INIT_3 = 0; 62 | defparam ram256x16_hi.INIT_4 = 0; 63 | defparam ram256x16_hi.INIT_5 = 0; 64 | defparam ram256x16_hi.INIT_6 = 0; 65 | defparam ram256x16_hi.INIT_7 = 0; 66 | defparam ram256x16_hi.INIT_8 = 256'hc19b_9bdc_80de_72be_550c_2431_1283_d807_ab1c_923f_59f1_3956_e9b5_b5c0_7137_428a; 67 | defparam ram256x16_hi.INIT_9 = 256'h1429_06ca_d5a7_c6e0_bf59_b003_a831_983e_76f9_5cb0_4a74_2de9_240c_0fc1_efbe_e49b; 68 | defparam ram256x16_hi.INIT_A = 256'h106a_f40e_d699_d192_c76c_c24b_a81a_a2bf_9272_81c2_766a_650a_5338_4d2c_2e1b_27b7; 69 | defparam ram256x16_hi.INIT_B = 256'hc671_bef9_a450_90be_8cc7_84c8_78a5_748f_682e_5b9c_4ed8_391c_34b0_2748_1e37_19a4; 70 | defparam ram256x16_hi.INIT_C = 0; 71 | defparam ram256x16_hi.INIT_D = 0; 72 | defparam ram256x16_hi.INIT_E = 0; 73 | defparam ram256x16_hi.INIT_F = 0; 74 | 75 | defparam ram256x16_lo.INIT_0 = 0; 76 | defparam ram256x16_lo.INIT_1 = 0; 77 | defparam ram256x16_lo.INIT_2 = 0; 78 | defparam ram256x16_lo.INIT_3 = 0; 79 | defparam ram256x16_lo.INIT_4 = 0; 80 | defparam ram256x16_lo.INIT_5 = 0; 81 | defparam ram256x16_lo.INIT_6 = 0; 82 | defparam ram256x16_lo.INIT_7 = 0; 83 | defparam ram256x16_lo.INIT_8 = 256'hf174_06a7_b1fe_5d74_7dc3_85be_5b01_aa98_5ed5_82a4_11f1_c25b_dba5_fbcf_4491_2f98; 84 | defparam ram256x16_lo.INIT_9 = 256'h2967_6351_9147_0bf3_7fc7_27c8_c66d_5152_88da_a9dc_84aa_2c6f_a1cc_9dc6_4786_69c1; 85 | defparam ram256x16_lo.INIT_A = 256'ha070_3585_0624_e819_51a3_8b70_664b_e8a1_2c85_c92e_0abb_7354_0d13_6dfc_2138_0a85; 86 | defparam ram256x16_lo.INIT_B = 256'h78f2_a3f7_6ceb_fffa_0208_7814_636f_82ee_6ff3_ca4f_aa4a_0cb3_bcb5_774c_6c08_c116; 87 | defparam ram256x16_lo.INIT_C = 0; 88 | defparam ram256x16_lo.INIT_D = 0; 89 | defparam ram256x16_lo.INIT_E = 0; 90 | defparam ram256x16_lo.INIT_F = 0; 91 | 92 | `else 93 | 94 | // Simulate RAM lookup for testing 95 | 96 | output reg [31:0] Kt; 97 | 98 | wire [31:0] Ks[0:63]; 99 | 100 | always @(posedge clk) 101 | begin 102 | Kt <= Ks[round]; 103 | end 104 | 105 | assign Ks[ 0] = 32'h428a2f98; 106 | assign Ks[ 1] = 32'h71374491; 107 | assign Ks[ 2] = 32'hb5c0fbcf; 108 | assign Ks[ 3] = 32'he9b5dba5; 109 | assign Ks[ 4] = 32'h3956c25b; 110 | assign Ks[ 5] = 32'h59f111f1; 111 | assign Ks[ 6] = 32'h923f82a4; 112 | assign Ks[ 7] = 32'hab1c5ed5; 113 | assign Ks[ 8] = 32'hd807aa98; 114 | assign Ks[ 9] = 32'h12835b01; 115 | assign Ks[10] = 32'h243185be; 116 | assign Ks[11] = 32'h550c7dc3; 117 | assign Ks[12] = 32'h72be5d74; 118 | assign Ks[13] = 32'h80deb1fe; 119 | assign Ks[14] = 32'h9bdc06a7; 120 | assign Ks[15] = 32'hc19bf174; 121 | assign Ks[16] = 32'he49b69c1; 122 | assign Ks[17] = 32'hefbe4786; 123 | assign Ks[18] = 32'h0fc19dc6; 124 | assign Ks[19] = 32'h240ca1cc; 125 | assign Ks[20] = 32'h2de92c6f; 126 | assign Ks[21] = 32'h4a7484aa; 127 | assign Ks[22] = 32'h5cb0a9dc; 128 | assign Ks[23] = 32'h76f988da; 129 | assign Ks[24] = 32'h983e5152; 130 | assign Ks[25] = 32'ha831c66d; 131 | assign Ks[26] = 32'hb00327c8; 132 | assign Ks[27] = 32'hbf597fc7; 133 | assign Ks[28] = 32'hc6e00bf3; 134 | assign Ks[29] = 32'hd5a79147; 135 | assign Ks[30] = 32'h06ca6351; 136 | assign Ks[31] = 32'h14292967; 137 | assign Ks[32] = 32'h27b70a85; 138 | assign Ks[33] = 32'h2e1b2138; 139 | assign Ks[34] = 32'h4d2c6dfc; 140 | assign Ks[35] = 32'h53380d13; 141 | assign Ks[36] = 32'h650a7354; 142 | assign Ks[37] = 32'h766a0abb; 143 | assign Ks[38] = 32'h81c2c92e; 144 | assign Ks[39] = 32'h92722c85; 145 | assign Ks[40] = 32'ha2bfe8a1; 146 | assign Ks[41] = 32'ha81a664b; 147 | assign Ks[42] = 32'hc24b8b70; 148 | assign Ks[43] = 32'hc76c51a3; 149 | assign Ks[44] = 32'hd192e819; 150 | assign Ks[45] = 32'hd6990624; 151 | assign Ks[46] = 32'hf40e3585; 152 | assign Ks[47] = 32'h106aa070; 153 | assign Ks[48] = 32'h19a4c116; 154 | assign Ks[49] = 32'h1e376c08; 155 | assign Ks[50] = 32'h2748774c; 156 | assign Ks[51] = 32'h34b0bcb5; 157 | assign Ks[52] = 32'h391c0cb3; 158 | assign Ks[53] = 32'h4ed8aa4a; 159 | assign Ks[54] = 32'h5b9cca4f; 160 | assign Ks[55] = 32'h682e6ff3; 161 | assign Ks[56] = 32'h748f82ee; 162 | assign Ks[57] = 32'h78a5636f; 163 | assign Ks[58] = 32'h84c87814; 164 | assign Ks[59] = 32'h8cc70208; 165 | assign Ks[60] = 32'h90befffa; 166 | assign Ks[61] = 32'ha4506ceb; 167 | assign Ks[62] = 32'hbef9a3f7; 168 | assign Ks[63] = 32'hc67178f2; 169 | 170 | `endif 171 | endmodule 172 | -------------------------------------------------------------------------------- /src/external_io.v: -------------------------------------------------------------------------------- 1 | /* interface 2 | * 3 | * IO interface for the top level module. 4 | */ 5 | module external_io( 6 | clk, 7 | reset_n, 8 | // SPI(0) 9 | sck0, 10 | sdi0, 11 | cs0_n, 12 | // SPI(1) 13 | sck1, 14 | sdi1, 15 | sdo1, 16 | cs1_n, 17 | // Stored data 18 | device_config, 19 | job_config, 20 | // Control signals 21 | core_reset_n, 22 | // From shapool 23 | shapool_success, 24 | shapool_result, 25 | // READY signal 26 | ready 27 | ); 28 | 29 | parameter POOL_SIZE = 2; 30 | parameter POOL_SIZE_LOG2 = 1; 31 | 32 | parameter DEVICE_CONFIG_WIDTH = 8; // nonce_start 33 | parameter JOB_CONFIG_WIDTH = 256 + 96; // sha_state + message_head 34 | parameter RESULT_DATA_WIDTH = 32 + 8; // nonce + match flags 35 | // FUTURE scale match_flags? 36 | 37 | // Inputs and outputs 38 | 39 | input wire clk; 40 | input wire reset_n; 41 | 42 | input wire sck0; 43 | input wire sdi0; 44 | input wire cs0_n; 45 | 46 | input wire sck1; 47 | input wire sdi1; 48 | output wire sdo1; 49 | input wire cs1_n; 50 | 51 | // Stored data 52 | output reg [DEVICE_CONFIG_WIDTH-1 : 0] device_config = 0; 53 | output reg [ JOB_CONFIG_WIDTH-1 : 0] job_config = 0; 54 | // output reg [ JOB_CONFIG_WIDTH-1 : 0] job_config = { 55 | // 128'hdc6a3b8d_0c69421a_cb1a5434_e536f7d5, // SHA starting state 56 | // 128'hc3c1b9e4_4cbb9b8f_95f0172e_fc48d2df, // ... 57 | // 96'hdc141787_358b0553_535f0119 // Start of message block 58 | // }; 59 | 60 | output reg core_reset_n = 1; 61 | 62 | reg [RESULT_DATA_WIDTH-1 : 0] result_data = 0; 63 | 64 | // From shapool 65 | input wire shapool_success; 66 | input wire [RESULT_DATA_WIDTH-1:0] shapool_result; 67 | 68 | // READY signal 69 | output reg ready; 70 | 71 | // State machine definition 72 | localparam STATE_RESET = 2'b00, 73 | STATE_LOAD = 2'b01, 74 | STATE_EXEC = 2'b10, 75 | STATE_DONE = 2'b11; 76 | 77 | reg [1:0] state = STATE_RESET; 78 | 79 | // Synchronize SPI signals to reference `clk` 80 | 81 | reg [2:0] sck0_sync = 0; 82 | reg [1:0] sdi0_sync = 0; 83 | wire sck0_sync_rising_edge; 84 | 85 | reg [2:0] sck1_sync = 0; 86 | reg [1:0] sdi1_sync = 0; 87 | wire sck1_sync_rising_edge; 88 | 89 | always @(posedge clk) 90 | begin 91 | sck0_sync <= { sck0_sync[1], sck0_sync[0], sck0 }; 92 | sck1_sync <= { sck1_sync[1], sck1_sync[0], sck1 }; 93 | 94 | sdi0_sync <= { sdi0_sync[0], sdi0 }; 95 | sdi1_sync <= { sdi1_sync[0], sdi1 }; 96 | end 97 | 98 | assign sck0_sync_rising_edge = ~sck0_sync[2] & sck0_sync[1]; 99 | assign sck1_sync_rising_edge = ~sck1_sync[2] & sck1_sync[1]; 100 | 101 | // `sdo1` comes from `result_data` when STATE_DONE, otherwise `device_config` 102 | assign sdo1 = (state == STATE_DONE) 103 | ? result_data[RESULT_DATA_WIDTH-1] 104 | : device_config[DEVICE_CONFIG_WIDTH-1]; 105 | 106 | // Main state machine process 107 | always @(posedge clk) 108 | begin 109 | case(state) 110 | 111 | STATE_RESET: 112 | begin 113 | ready <= 0; // Deassert READY 114 | core_reset_n <= 0; // Halt core 115 | state <= STATE_LOAD; 116 | end 117 | 118 | STATE_LOAD: 119 | begin 120 | 121 | // Go to STATE_EXEC when `reset_n` is deasserted 122 | if (reset_n) 123 | begin 124 | core_reset_n <= 1; 125 | state <= STATE_EXEC; 126 | end 127 | 128 | // Allow `job_config` and `device_config` to be shifted in while `reset_n` is asserted. 129 | else 130 | begin 131 | // SPI0 132 | if (!cs0_n && sck0_sync_rising_edge) 133 | job_config <= { job_config[JOB_CONFIG_WIDTH-2 : 0], sdi0_sync[1] }; 134 | 135 | // SPI1 136 | if (!cs1_n && sck1_sync_rising_edge) 137 | device_config <= { device_config[DEVICE_CONFIG_WIDTH-2 : 0], sdi1_sync[1] }; 138 | end 139 | end 140 | 141 | STATE_EXEC: 142 | begin 143 | core_reset_n <= 1; 144 | 145 | // Go to STATE_RESET when `reset_n` is asserted 146 | if (!reset_n) 147 | begin 148 | state <= STATE_RESET; 149 | end 150 | // Go to STATE_DONE when `shapool_success` is asserted 151 | else if (shapool_success) 152 | begin 153 | state <= STATE_DONE; 154 | ready <= 1; 155 | core_reset_n <= 0; 156 | 157 | /* verilator lint_off WIDTHCONCAT */ 158 | 159 | // NOTE: The top POOL_SIZE_LOG2 bits are zeroed. 160 | // Host needs to perform check on all possible combination 161 | // of these bits. 162 | 163 | // NOTE: When "success" occurs, the current nonce value is 164 | // one value ahead of the nonce value that caused the success. 165 | // This is because the nonce value feeds the first hash unit, 166 | // whereas success is determined by the result of the 167 | // second. 168 | // In order to save resources, the host is responsible for 169 | // correcting this offset. 170 | result_data <= shapool_result; 171 | /* verilator lint_on WIDTHCONCAT */ 172 | end 173 | // Go to STATE_DONE when `cs1_n` is asserted 174 | // FUTURE neighbour READY 175 | else if (!cs1_n) 176 | begin 177 | state <= STATE_DONE; 178 | ready <= 1; 179 | core_reset_n <= 0; 180 | result_data <= 0; 181 | end 182 | end 183 | 184 | STATE_DONE: 185 | begin 186 | if (!reset_n) 187 | state <= STATE_RESET; 188 | 189 | // Shift-in `result_data` (msb-first) on rising edge 190 | if (!cs1_n && sck1_sync_rising_edge) 191 | result_data <= { result_data[RESULT_DATA_WIDTH-2 : 0], sdi1_sync[1] }; 192 | 193 | end 194 | 195 | default: 196 | state <= STATE_RESET; 197 | 198 | endcase 199 | end 200 | 201 | endmodule -------------------------------------------------------------------------------- /src/pinout-ice40hx8k-b-evn.pcf: -------------------------------------------------------------------------------- 1 | # Pinout configuration for the Lattice Semiconductor iCE40HX8K Evaluation Board. 2 | # * http://www.latticesemi.com/en/Products/DevelopmentBoardsAndKits/iCE40HX8KBreakoutBoard.aspx 3 | 4 | # External oscillator (12 MHz) 5 | set_io clk_in J13 6 | 7 | # External asynchronous reset 8 | set_io reset_n_in B15 9 | 10 | # SPI0 (Shared SPI interface) 11 | set_io sck0_in R11 12 | set_io sdi0_in P11 13 | #set_io sdo0_out T11 14 | set_io cs0_n_in R12 15 | 16 | # SPI1 (Daisy-chained SPI interface) 17 | set_io sck1_in B10 18 | set_io sdi1_in B12 19 | set_io sdo1_out B13 20 | set_io cs1_n_in A16 21 | 22 | # READY host open-drain interrupt 23 | set_io ready_n_od_out B14 24 | 25 | # Status/Heartbeat LED 26 | set_io status_led_n_out B5 -------------------------------------------------------------------------------- /src/pinout-icepool-ice40hx8k.pcf: -------------------------------------------------------------------------------- 1 | # Pinout configuration for the `icepool` ice40hx8k-bg121 daughter board. 2 | # * https://github.com/jkiv/icepool-board 3 | 4 | # Pinout for the BG121 variant of the ICE40HX8K. 5 | 6 | # External oscillator (12 MHz) 7 | set_io clk_in F11 8 | 9 | # External asynchronous reset 10 | set_io reset_n_in J1 11 | 12 | # SPI0 (Shared SPI interface) 13 | set_io sck0_in L10 14 | set_io sdi0_in J9 15 | #set_io sdo0_out K9 16 | set_io cs0_n_in K10 17 | 18 | # SPI1 (Daisy-chained SPI interface) 19 | set_io sck1_in G1 20 | set_io sdi1_in F3 21 | set_io sdo1_out K1 22 | set_io cs1_n_in H1 23 | 24 | # READY host open-drain interrupt 25 | set_io ready_n_od_out B1 26 | 27 | # Status/Heartbeat LED 28 | set_io status_led_n_out A11 29 | -------------------------------------------------------------------------------- /src/pinout-icepool-ice40up5k.pcf: -------------------------------------------------------------------------------- 1 | # Pinout configuration for the `icepool` ice40up5k-sg48 daughter board. 2 | # * https://github.com/jkiv/icepool-board 3 | 4 | # FUTURE 5 | 6 | # External oscillator (12 MHz) 7 | set_io clk_in 35 8 | 9 | # External asynchronous reset 10 | set_io reset_n_in 4 11 | 12 | # SPI0 (Shared SPI interface) 13 | set_io sck0_in 15 14 | set_io sdi0_in 17 15 | #set_io sdo0_out 18 16 | set_io cs0_n_in 16 17 | 18 | # SPI1 (Daisy-chained SPI interface) 19 | set_io sck1_in 20 | set_io sdi1_in 21 | set_io sdo1_out 22 | set_io cs1_n_in 23 | 24 | # READY host open-drain interrupt 25 | set_io ready_n_od_out 26 | 27 | # Status/Heartbeat LED 28 | set_io status_led_n_out 39 29 | -------------------------------------------------------------------------------- /src/sha_round.v: -------------------------------------------------------------------------------- 1 | /* sha_round 2 | * 3 | * Asynchronously performs a single SHA256 round given an input state and 4 | * values for Kt and Wt for the current round. 5 | * 6 | * For SHA-256, 7 | * 8 | * T1 = h + BSIG1(e) + CH(e,f,g) + Kt + Wt 9 | * T2 = BSIG0(a) + MAJ(a,b,c) 10 | * h' = g 11 | * g' = f 12 | * f' = e 13 | * e' = d + T1 14 | * d' = c 15 | * c' = b 16 | * b' = a 17 | * a' = T1 + T2 18 | * 19 | * where Kt is a constant for loop iteration t 20 | * Wt is the message scheudle value for iteration t 21 | * a -> h are input working words 22 | * a' -> h' are output working words 23 | */ 24 | module sha_round(in, Kt, Wt, out); 25 | 26 | `define SHA_ROUND_USE_OPTIMIZED_EXPRESSIONS 27 | 28 | input wire [ 31:0] Kt; // constant word (iteration t) 29 | input wire [ 31:0] Wt; // message schedule word (iteration t) 30 | input wire [255:0] in; // working words, a -> h 31 | output wire [255:0] out; // resultant working words a' -> h' 32 | 33 | wire [31:0] a, b, c, d, e, f, g, h; // working word aliases 34 | wire [31:0] bsig0_a; // result of BSIG0(a) 35 | wire [31:0] bsig1_e; // result of BSIG1(e) 36 | wire [31:0] ch_e_f_g; // result of CH(e,f,g) 37 | wire [31:0] maj_a_b_c; // result of MAJ(a,b,c) 38 | wire [31:0] t1, t2; // temporary values 39 | 40 | // Break up input bus into working variables 41 | assign a = in[255:224]; 42 | assign b = in[223:192]; 43 | assign c = in[191:160]; 44 | assign d = in[159:128]; 45 | assign e = in[127: 96]; 46 | assign f = in[ 95: 64]; 47 | assign g = in[ 63: 32]; 48 | assign h = in[ 31: 0]; 49 | 50 | // For SHA256, BSIG0(X) = ROTR2(X) XOR ROTR13(X) XOR ROTR22(X) 51 | assign bsig0_a = { a[ 1:0], a[31: 2] } 52 | ^ { a[12:0], a[31:13] } 53 | ^ { a[21:0], a[31:22] }; 54 | 55 | // For SHA256, BSIG1(X) = ROTR6(X) XOR ROTR11(X) XOR ROTR25(X) 56 | assign bsig1_e = { e[ 5:0], e[31: 6] } 57 | ^ { e[10:0], e[31:11] } 58 | ^ { e[24:0], e[31:25] }; 59 | 60 | // For SHA256, CH(X,Y,Z) = (X AND Y) XOR ((NOT X) AND Z) 61 | `ifdef SHA_ROUND_USE_OPTIMIZED_EXPRESSIONS 62 | assign ch_e_f_g = g ^ (e & (g ^ f)); 63 | `else 64 | assign ch_e_f_g = (e & f) ^ ((~e) & g); 65 | `endif 66 | 67 | // For SHA256, MAJ(X,Y,Z) = (X AND Y) XOR (X AND Z) XOR (Y AND Z) 68 | `ifdef SHA_ROUND_USE_OPTIMIZED_EXPRESSIONS 69 | assign maj_a_b_c = ((b & c) | (a & (b | c))); 70 | `else 71 | assign maj_a_b_c = (a & b) ^ (a & c) ^ (b & c); 72 | `endif 73 | 74 | // Update temporary values 75 | assign t1 = h + bsig1_e + ch_e_f_g + Kt + Wt; 76 | assign t2 = bsig0_a + maj_a_b_c; 77 | 78 | // Update working words for next stage/iteration 79 | assign out[255:224] = t1 + t2; // a' = T1 + T2 80 | assign out[223:192] = a; // b' = a 81 | assign out[191:160] = b; // c' = b 82 | assign out[159:128] = c; // d' = c 83 | assign out[127: 96] = d + t1; // e' = d + T1 84 | assign out[ 95: 64] = e; // f' = e 85 | assign out[ 63: 32] = f; // g' = f 86 | assign out[ 31: 0] = g; // h' = g 87 | 88 | endmodule // sha_iter 89 | -------------------------------------------------------------------------------- /src/sha_unit.v: -------------------------------------------------------------------------------- 1 | module sha_unit( 2 | // Control 3 | clk, 4 | // Externally managed state 5 | round, 6 | Kt, 7 | // SHA256 parameters 8 | M, 9 | H0, 10 | // Result 11 | H1 12 | ); 13 | 14 | `define idx32(x) (32*(x+1)-1):(32*(x)) 15 | 16 | input wire clk; 17 | 18 | input wire [5:0] round; 19 | input wire [31:0] Kt; 20 | 21 | input wire [511:0] M; 22 | input wire [255:0] H0; 23 | output wire [255:0] H1; 24 | 25 | reg [31:0] Wt = 0; 26 | reg [479:0] W = 0; 27 | wire [31:0] Wxt; 28 | 29 | // Message as 16x 32-bit blocks 30 | // TODO Mt in block RAM, as input 31 | wire [31:0] Mt[0:15]; 32 | 33 | reg [255:0] S0; 34 | wire [255:0] S1; 35 | 36 | w_expand wx( 37 | W[`idx32(16-2)], 38 | W[`idx32(16-7)], 39 | W[`idx32(16-15)], 40 | W[`idx32(16-16)], 41 | Wxt 42 | ); 43 | 44 | // Concurrent SHA256 round module 45 | sha_round sr( 46 | S0, 47 | Kt, 48 | Wt, 49 | S1 50 | ); 51 | 52 | always @(posedge clk) 53 | begin 54 | if (round == 0) 55 | S0 <= H0; 56 | else 57 | S0 <= S1; 58 | end 59 | 60 | always @(posedge clk) 61 | begin 62 | if (round < 16) 63 | Wt <= Mt[round[3:0]]; 64 | else 65 | Wt <= Wxt; 66 | end 67 | 68 | always @(posedge clk) 69 | begin 70 | W <= { Wt, W[479:32] }; 71 | end 72 | 73 | assign H1[`idx32(7)] = S1[`idx32(7)] + H0[`idx32(7)]; 74 | assign H1[`idx32(6)] = S1[`idx32(6)] + H0[`idx32(6)]; 75 | assign H1[`idx32(5)] = S1[`idx32(5)] + H0[`idx32(5)]; 76 | assign H1[`idx32(4)] = S1[`idx32(4)] + H0[`idx32(4)]; 77 | assign H1[`idx32(3)] = S1[`idx32(3)] + H0[`idx32(3)]; 78 | assign H1[`idx32(2)] = S1[`idx32(2)] + H0[`idx32(2)]; 79 | assign H1[`idx32(1)] = S1[`idx32(1)] + H0[`idx32(1)]; 80 | assign H1[`idx32(0)] = S1[`idx32(0)] + H0[`idx32(0)]; 81 | 82 | assign Mt[ 0] = M[`idx32(15)]; 83 | assign Mt[ 1] = M[`idx32(14)]; 84 | assign Mt[ 2] = M[`idx32(13)]; 85 | assign Mt[ 3] = M[`idx32(12)]; 86 | assign Mt[ 4] = M[`idx32(11)]; 87 | assign Mt[ 5] = M[`idx32(10)]; 88 | assign Mt[ 6] = M[`idx32( 9)]; 89 | assign Mt[ 7] = M[`idx32( 8)]; 90 | assign Mt[ 8] = M[`idx32( 7)]; 91 | assign Mt[ 9] = M[`idx32( 6)]; 92 | assign Mt[10] = M[`idx32( 5)]; 93 | assign Mt[11] = M[`idx32( 4)]; 94 | assign Mt[12] = M[`idx32( 3)]; 95 | assign Mt[13] = M[`idx32( 2)]; 96 | assign Mt[14] = M[`idx32( 1)]; 97 | assign Mt[15] = M[`idx32( 0)]; 98 | 99 | endmodule 100 | -------------------------------------------------------------------------------- /src/shapool.v: -------------------------------------------------------------------------------- 1 | module shapool 2 | ( 3 | // Control 4 | clk, 5 | reset_n, 6 | // Job Params 7 | sha_state, 8 | message_head, 9 | nonce_start_MSB, 10 | // Result 11 | success, 12 | nonce, 13 | match_flags 14 | ); 15 | parameter POOL_SIZE = 2; 16 | parameter POOL_SIZE_LOG2 = 1; 17 | parameter BASE_TARGET = 32; 18 | 19 | localparam NONCE_LOWER_WIDTH = 32 - POOL_SIZE_LOG2; 20 | 21 | input wire clk; 22 | input wire reset_n; 23 | 24 | // Job parameters 25 | input wire [255:0] sha_state; 26 | input wire [95:0] message_head; 27 | input wire [7:0] nonce_start_MSB; 28 | 29 | // Job results 30 | output wire success; 31 | output wire [31:0] nonce; 32 | output wire [7:0] match_flags; 33 | 34 | // Shared state 35 | reg [NONCE_LOWER_WIDTH-1:0] nonce_lower = 0; 36 | reg [5:0] round = 0; 37 | 38 | // Supress some logic during first and second hash 39 | // - `first_hash_complete` allows us to supress logic before first hash completes. 40 | // - `second_hash_complete` allows us to supress logic before second hash completes. 41 | reg first_hash_complete = 0; 42 | reg second_hash_complete = 0; 43 | 44 | localparam[255:0] SHA256_H0 = { 45 | 32'h6a09e667, 32'hbb67ae85, 46 | 32'h3c6ef372, 32'ha54ff53a, 47 | 32'h510e527f, 32'h9b05688c, 48 | 32'h1f83d9ab, 32'h5be0cd19 49 | }; 50 | 51 | wire [31:0] Kt; 52 | 53 | SHA256_K K ( 54 | clk, 55 | round, 56 | Kt 57 | ); 58 | 59 | localparam [383:0] M0_tail = { 60 | 128'h80000000_00000000_00000000_00000000, 61 | 128'h00000000_00000000_00000000_00000000, 62 | 128'h00000000_00000000_00000000_00000280 63 | }; 64 | 65 | localparam [255:0] M1_tail = { 66 | 128'h80000000_00000000_00000000_00000000, 67 | 128'h00000000_00000000_00000000_00000100 68 | }; 69 | 70 | /** Generate "pipelines". 71 | 72 | Each pipeline: 73 | * consists of two chained `sha_unit` hashing units. 74 | * hardcodes unique nonce most-significant bits. 75 | * checks for leading zeros and generates its own `match_flag`. 76 | 77 | */ 78 | genvar n; 79 | generate 80 | for (n = 0; n < POOL_SIZE; n = n + 1) 81 | begin : pipelines 82 | 83 | // Hash inputs 84 | wire [511:0] M0; 85 | wire [511:0] M1; 86 | 87 | // Hash outputs 88 | wire [255:0] H_u0; 89 | wire [255:0] H_u1; 90 | 91 | // Saved bits from H_u0 for M1 92 | reg [223:0] M1_H1; 93 | 94 | // Byte-swapped H_u1 for testing 95 | /* verilator lint_off UNUSED */ 96 | wire [255:0] H_u1_swap; 97 | /* verilator lint_on UNUSED */ 98 | 99 | // Bits to test for `success` 100 | wire [BASE_TARGET-1:0] H_test_bits; 101 | 102 | `ifndef SHAPOOL_NO_NONCE_OFFSET 103 | // Hard-coded unit offset 104 | // -- reduces size of nonce register and increment logic 105 | // -- POOL_SIZE must be a power of 2 106 | // -- POOL_SIZE_LOG2 must be > 0 107 | wire [POOL_SIZE_LOG2-1:0] nonce_upper; 108 | assign nonce_upper = n; 109 | `endif 110 | 111 | // Construct `M_nonce`: 112 | // 113 | // 31 31-POOL_SIZE_LOG2 0 114 | // +-------------+-----------------------------------------+ 115 | // | nonce_upper | nonce_lower | 116 | // +-------------+-----------------------------------------+ 117 | // XOR 118 | // +-------------------------+-----------------------------+ 119 | // | nonce_start_MSB | 0 ... 0 | 120 | // +-------------------------+-----------------------------+ 121 | // FUTURE Nonce space segmenting issues arise when not all devices 122 | // have the same `POOL_SIZE`. 123 | 124 | wire [31:0] M_nonce; 125 | 126 | `ifdef SHAPOOL_NO_NONCE_OFFSET 127 | assign M_nonce = nonce_lower ^ { nonce_start_MSB, 24'b0 }; 128 | `else 129 | assign M_nonce = { nonce_upper, nonce_lower } ^ { nonce_start_MSB, 24'b0 }; 130 | `endif 131 | 132 | assign M0 = { 133 | // Start of M0 134 | message_head, 135 | // Nonce (swapped endianness) 136 | { 137 | M_nonce[ 7: 0], 138 | M_nonce[15: 8], 139 | M_nonce[23:16], 140 | M_nonce[31:24] 141 | }, 142 | // End of M0 143 | M0_tail 144 | }; 145 | 146 | // SHA256 unit for first hash 147 | sha_unit u0( 148 | clk, 149 | round, 150 | Kt, 151 | M0, 152 | sha_state, 153 | H_u0 154 | ); 155 | 156 | // Save `H_u0` (output of first stage) for use in `M1_H1`. 157 | // Bits `H_u0[255:224]` are used by `u1` at the same moment and sent directly to input, 158 | // so we don't need to store them in M1_H1. 159 | // 160 | // `H_u0[255:224]` is needed as soon as it is available, when `round` 161 | // goes from `0` to `1`. At the same instant, the rest of `H_u0` is stored 162 | // in `M1_H1` for use in subsequent rounds. 163 | always @(posedge clk) 164 | begin 165 | if (round == 0) 166 | M1_H1 <= H_u0[223:0]; 167 | end 168 | 169 | assign M1 = { 170 | H_u0[255:224], 171 | M1_H1, 172 | M1_tail 173 | }; 174 | 175 | // SHA256 unit for second hash 176 | sha_unit u1( 177 | clk, 178 | round, 179 | Kt, 180 | M1, 181 | SHA256_H0, 182 | H_u1 183 | ); 184 | 185 | // Endianness-swapped hash result for final comparison. 186 | // -- Depending on target, lower bits may not be used. Hopefully 187 | // logic for generating superfluous bits are optimized out. 188 | assign H_u1_swap = { 189 | H_u1[ 7: 0], H_u1[ 15: 8], H_u1[ 23: 16], H_u1[ 31: 24], 190 | H_u1[ 39: 32], H_u1[ 47: 40], H_u1[ 55: 48], H_u1[ 63: 56], 191 | H_u1[ 71: 64], H_u1[ 79: 72], H_u1[ 87: 80], H_u1[ 95: 88], 192 | H_u1[103: 96], H_u1[111:104], H_u1[119:112], H_u1[127:120], 193 | H_u1[135:128], H_u1[143:136], H_u1[151:144], H_u1[159:152], 194 | H_u1[167:160], H_u1[175:168], H_u1[183:176], H_u1[191:184], 195 | H_u1[199:192], H_u1[207:200], H_u1[215:208], H_u1[223:216], 196 | H_u1[231:224], H_u1[239:232], H_u1[247:240], H_u1[255:248] 197 | }; 198 | 199 | // Bits of result we care about 200 | assign H_test_bits = H_u1_swap[255:256-BASE_TARGET]; 201 | 202 | // Test bits for zero 203 | assign match_flags[n] = ~(|H_test_bits); 204 | 205 | end 206 | endgenerate 207 | 208 | // Zero out top of `match_flags` 209 | assign match_flags[7:POOL_SIZE] = 0; 210 | 211 | // Generate `success` flag 212 | assign success = (|match_flags[POOL_SIZE-1:0]) & second_hash_complete & (round == 0); 213 | 214 | // Output current nonce 215 | `ifdef SHAPOOL_NO_NONCE_OFFSET 216 | assign nonce = nonce_lower; 217 | `else 218 | assign nonce = {{(POOL_SIZE_LOG2){1'b0}}, nonce_lower}; 219 | `endif 220 | 221 | // Control `first_hash_complete` flag 222 | always @(posedge clk) 223 | begin 224 | if (!reset_n) 225 | first_hash_complete <= 0; 226 | else if (round == 63 && !first_hash_complete) 227 | first_hash_complete <= 1; 228 | end 229 | 230 | // Control `second_hash_complete` flag 231 | always @(posedge clk) 232 | begin 233 | if (!reset_n) 234 | second_hash_complete <= 0; 235 | else if (round == 63 && first_hash_complete) 236 | second_hash_complete <= 1; 237 | end 238 | 239 | // Control `round` 240 | always @(posedge clk) 241 | begin 242 | if (!reset_n) 243 | round <= 0; 244 | else 245 | round <= round + 1; // mod 63 246 | end 247 | 248 | // Control `nonce` 249 | always @(posedge clk) 250 | begin 251 | if (!reset_n) 252 | nonce_lower <= 0; 253 | else if (round == 63) 254 | nonce_lower <= nonce_lower + 1; 255 | end 256 | 257 | endmodule 258 | -------------------------------------------------------------------------------- /src/targets/target-1.vh: -------------------------------------------------------------------------------- 1 | `define TARGET 32 2 | -------------------------------------------------------------------------------- /src/targets/target-1024.vh: -------------------------------------------------------------------------------- 1 | `define TARGET 42 2 | -------------------------------------------------------------------------------- /src/targets/target-128.vh: -------------------------------------------------------------------------------- 1 | `define TARGET 39 2 | -------------------------------------------------------------------------------- /src/targets/target-16.vh: -------------------------------------------------------------------------------- 1 | `define TARGET 36 2 | -------------------------------------------------------------------------------- /src/targets/target-2.vh: -------------------------------------------------------------------------------- 1 | `define TARGET 33 2 | -------------------------------------------------------------------------------- /src/targets/target-2048.vh: -------------------------------------------------------------------------------- 1 | `define TARGET 43 2 | -------------------------------------------------------------------------------- /src/targets/target-256.vh: -------------------------------------------------------------------------------- 1 | `define TARGET 40 2 | -------------------------------------------------------------------------------- /src/targets/target-32.vh: -------------------------------------------------------------------------------- 1 | `define TARGET 37 2 | -------------------------------------------------------------------------------- /src/targets/target-4.vh: -------------------------------------------------------------------------------- 1 | `define TARGET 34 2 | -------------------------------------------------------------------------------- /src/targets/target-4096.vh: -------------------------------------------------------------------------------- 1 | `define TARGET 44 2 | -------------------------------------------------------------------------------- /src/targets/target-512.vh: -------------------------------------------------------------------------------- 1 | `define TARGET 41 2 | -------------------------------------------------------------------------------- /src/targets/target-64.vh: -------------------------------------------------------------------------------- 1 | `define TARGET 38 2 | -------------------------------------------------------------------------------- /src/targets/target-8.vh: -------------------------------------------------------------------------------- 1 | `define TARGET 35 2 | -------------------------------------------------------------------------------- /src/targets/target-8192.vh: -------------------------------------------------------------------------------- 1 | `define TARGET 45 2 | -------------------------------------------------------------------------------- /src/tests/Makefile: -------------------------------------------------------------------------------- 1 | .phony : run 2 | run: sha_round_usage_tb sha_unit_usage_tb shapool_usage_tb external_io_usage_tb 3 | 4 | .phony : hardware_tests 5 | hardware_tests : btc_four_zeroes 6 | 7 | .phony : all 8 | all : run hardware_tests 9 | 10 | .phony : clean 11 | clean : clean_test_benches clean_hardware_tests 12 | 13 | .phony : clean_test_benches 14 | clean_test_benches : 15 | rm -f *.out *.vcd 16 | 17 | .phony : clean_hardware_tests 18 | clean_hardware_tests : 19 | make -C ./btc_four_zeroes clean 20 | 21 | # Testbench 22 | 23 | sha_round_usage_tb.out : sha_round_usage_tb.v ../sha_round.v 24 | iverilog -o $@ $^ 25 | 26 | sha_round_usage_tb : sha_round_usage_tb.out 27 | vvp $^ 28 | 29 | ## 30 | 31 | sha_unit_usage_tb.out : sha_unit_usage_tb.v ../sha_unit.v ../w_expand.v ../sha_round.v 32 | iverilog -o $@ $^ 33 | 34 | sha_unit_usage_tb : sha_unit_usage_tb.out 35 | vvp $^ 36 | 37 | ## 38 | 39 | shapool_usage_tb.out : shapool_usage_tb.v ../shapool.v ../SHA256_K.v ../sha_unit.v ../w_expand.v ../sha_round.v 40 | iverilog -o $@ $^ 41 | 42 | shapool_usage_tb : shapool_usage_tb.out 43 | vvp $^ 44 | 45 | ## 46 | 47 | external_io_usage_tb.out : external_io_usage_tb.v ../external_io.v 48 | iverilog -o $@ $^ 49 | 50 | external_io_usage_tb : external_io_usage_tb.out 51 | vvp $^ 52 | 53 | # Tests requiring hardware 54 | 55 | .phony : btc_four_zeroes 56 | btc_four_zeroes : 57 | make -C ./btc_four_zeroes run -------------------------------------------------------------------------------- /src/tests/btc_four_zeroes/Makefile: -------------------------------------------------------------------------------- 1 | VENDOR_PATH=../vendor/ 2 | MUNIT_C_PATH=../vendor/munit/munit.c 3 | ICEPOOL_PATH=../vendor/icepool-driver/src/ 4 | LIBICEPOOL_PATH=../vendor/icepool-driver/src/ 5 | FTDI_PATH=/usr/include/ftdi1/ 6 | LIBFTDI_PATH=/usr/lib/ 7 | 8 | .phony : build 9 | build : btc_four_zeroes gateware 10 | 11 | .phony : run 12 | run : build flash 13 | ./btc_four_zeroes 14 | 15 | btc_four_zeroes : main.c $(MUNIT_C_PATH) libicepool 16 | gcc --std=c99 -g -o $@ -I$(VENDOR_PATH) -I$(ICEPOOL_PATH) -I$(FTDI_PATH) $< $(MUNIT_C_PATH) -L$(LIBICEPOOL_PATH) -licepool-d -L$(LIBFTDI_PATH) -lftdi1 17 | 18 | .phony : libicepool 19 | libicepool : 20 | make -C $(ICEPOOL_PATH) libicepool-d.a 21 | 22 | .phony : gateware 23 | gateware : 24 | make -C ./rtl build 25 | 26 | .phony : flash 27 | flash : 28 | make -C rtl/ flash 29 | 30 | .phony : clean 31 | clean : 32 | make -C ./rtl clean 33 | rm -f ./btc_four_zeroes -------------------------------------------------------------------------------- /src/tests/btc_four_zeroes/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "munit/munit.h" 4 | #include "icepool.h" 5 | 6 | #define DEVICE_CONFIG_LEN (8/8) 7 | #define DEVICE_RESULT_LEN (40/8) 8 | #define JOB_CONFIG_LEN (352/8) 9 | 10 | void print_buffer(const uint8_t* buffer, size_t buffer_len) 11 | { 12 | for(size_t i = 0; i < buffer_len; ) 13 | { 14 | for (size_t j = 0; j < 8 && i < buffer_len; j++, i++) 15 | { 16 | printf("%02x ", buffer[i]); 17 | } 18 | printf("\n"); 19 | } 20 | } 21 | 22 | void test_spi_daisy_no_exec(IcepoolContext* ctx) 23 | { 24 | // Set up device data 25 | uint8_t device_config[DEVICE_CONFIG_LEN] = { 0 }; 26 | 27 | uint8_t result[DEVICE_CONFIG_LEN] = { 0 }; 28 | 29 | // Try immediate read-after-write 30 | 31 | icepool_assert_reset(ctx); 32 | 33 | icepool_spi_assert_daisy(ctx); 34 | 35 | icepool_spi_write_daisy(ctx, device_config, DEVICE_CONFIG_LEN); 36 | 37 | icepool_spi_read_daisy(ctx, result, DEVICE_CONFIG_LEN); 38 | 39 | icepool_spi_deassert_daisy(ctx); 40 | 41 | munit_assert_memory_equal(DEVICE_CONFIG_LEN, device_config, result); 42 | } 43 | 44 | void test_spi_daisy_with_exec(IcepoolContext* ctx) 45 | { 46 | // Set up device data 47 | uint8_t device_config[DEVICE_CONFIG_LEN] = { 0 }; 48 | 49 | uint8_t result[DEVICE_CONFIG_LEN] = { 0 }; 50 | 51 | // Try EXEC and reset 52 | icepool_assert_reset(ctx); 53 | 54 | icepool_spi_assert_daisy(ctx); 55 | 56 | icepool_spi_write_daisy(ctx, device_config, DEVICE_CONFIG_LEN); 57 | 58 | icepool_spi_deassert_daisy(ctx); 59 | 60 | icepool_deassert_reset(ctx); 61 | 62 | // TODO usleep 63 | 64 | icepool_assert_reset(ctx); 65 | 66 | icepool_spi_assert_daisy(ctx); 67 | 68 | icepool_spi_read_daisy(ctx, result, DEVICE_CONFIG_LEN); 69 | 70 | icepool_spi_deassert_daisy(ctx); 71 | 72 | munit_assert_memory_equal(DEVICE_CONFIG_LEN, device_config, result); 73 | } 74 | 75 | void test_btc_four_zeroes(IcepoolContext *ctx) 76 | { 77 | // Set up device data 78 | uint8_t device_config[DEVICE_CONFIG_LEN] = { 0 }; 79 | 80 | // Set up job data 81 | uint8_t job_config[JOB_CONFIG_LEN] = { 82 | // SHA initial state: 83 | 0xdc, 0x6a, 0x3b, 0x8d, 0x0c, 0x69, 0x42, 0x1a, 84 | 0xcb, 0x1a, 0x54, 0x34, 0xe5, 0x36, 0xf7, 0xd5, 85 | 0xc3, 0xc1, 0xb9, 0xe4, 0x4c, 0xbb, 0x9b, 0x8f, 86 | 0x95, 0xf0, 0x17, 0x2e, 0xfc, 0x48, 0xd2, 0xdf, 87 | // Message head: 88 | 0xdc, 0x14, 0x17, 0x87, 0x35, 0x8b, 0x05, 0x53, 89 | 0x53, 0x5f, 0x01, 0x19, 90 | }; 91 | // Expected nonce: 39 92 | // Expected hash: c7f3244e501edf780c420f63a4266d30ffe1bdb53f4fde3ccd688604f15ffd03 93 | 94 | // Assert reset_n 95 | icepool_assert_reset(ctx); 96 | 97 | // Send device data 98 | icepool_spi_assert_daisy(ctx); 99 | 100 | icepool_spi_write_daisy(ctx, device_config, DEVICE_CONFIG_LEN); 101 | 102 | icepool_spi_deassert_daisy(ctx); 103 | 104 | // Send job data 105 | icepool_spi_assert_shared(ctx); 106 | 107 | icepool_spi_write_shared(ctx, job_config, JOB_CONFIG_LEN); 108 | 109 | icepool_spi_deassert_shared(ctx); 110 | 111 | // Deassert reset_n (start executing) 112 | icepool_deassert_reset(ctx); 113 | 114 | // Wait for READY 115 | bool ready = false; 116 | for (size_t i = 0; !ready && i < 1e9; i++) 117 | { 118 | ready = icepool_poll_ready(ctx); 119 | } 120 | 121 | munit_assert_true(ready); 122 | 123 | // Get result 124 | 125 | uint8_t expected_result[DEVICE_RESULT_LEN] = { 0x01, 0x00, 0x00, 0x00, 0x29 }; 126 | uint8_t result[DEVICE_RESULT_LEN] = { 0xFF }; 127 | 128 | icepool_spi_assert_daisy(ctx); 129 | 130 | icepool_spi_read_daisy(ctx, result, DEVICE_RESULT_LEN); 131 | 132 | icepool_spi_assert_daisy(ctx); 133 | 134 | // Assert reset_n 135 | icepool_assert_reset(ctx); 136 | 137 | print_buffer(result, DEVICE_RESULT_LEN); 138 | 139 | munit_assert_memory_equal(DEVICE_RESULT_LEN, result, expected_result); 140 | } 141 | 142 | int main() 143 | { 144 | // Set up icepool context 145 | IcepoolContext* ctx = icepool_new(); 146 | 147 | if (!ctx) { 148 | fprintf(stderr, "Could not initialize IcepoolContext. Quitting...\n"); 149 | exit(EXIT_FAILURE); 150 | } 151 | 152 | test_spi_daisy_no_exec(ctx); 153 | 154 | test_spi_daisy_with_exec(ctx); 155 | 156 | test_btc_four_zeroes(ctx); 157 | 158 | icepool_free(ctx); 159 | 160 | return EXIT_SUCCESS; 161 | } -------------------------------------------------------------------------------- /src/tests/btc_four_zeroes/rtl/Makefile: -------------------------------------------------------------------------------- 1 | SHAPOOL_PATH=../../../ 2 | SHAPOOL_DEPS=$(SHAPOOL_PATH)top.v \ 3 | $(SHAPOOL_PATH)shapool.v \ 4 | $(SHAPOOL_PATH)sha_unit.v \ 5 | $(SHAPOOL_PATH)sha_round.v \ 6 | $(SHAPOOL_PATH)w_expand.v \ 7 | $(SHAPOOL_PATH)SHA256_K.v \ 8 | $(SHAPOOL_PATH)external_io.v 9 | 10 | TEST_NAME=btc_four_zeroes 11 | SHAPOOL_PCF=$(SHAPOOL_PATH)pinout-icepool-ice40hx8k.pcf 12 | 13 | .phony : build 14 | build : $(TEST_NAME).bin 15 | 16 | .phony: flash 17 | flash : $(TEST_NAME).bin 18 | iceprog -v -S $^ 19 | 20 | $(TEST_NAME).bin : $(TEST_NAME).asc 21 | icepack $^ $@ 22 | 23 | $(TEST_NAME).asc : $(TEST_NAME).json $(SHAPOOL_PCF) 24 | nextpnr-ice40 --package bg121 --hx8k --json $(TEST_NAME).json --pcf $(SHAPOOL_PCF) --asc $@ 25 | 26 | $(TEST_NAME).json : ./top_test.v $(SHAPOOL_DEPS) 27 | yosys -p 'synth_ice40 -top top_test -json $@ -abc2 -retime' $^ 28 | 29 | # Clean up 30 | .phony: clean 31 | clean : 32 | rm -f $(TEST_NAME).out \ 33 | $(TEST_NAME).json \ 34 | $(TEST_NAME).asc \ 35 | $(TEST_NAME).bin -------------------------------------------------------------------------------- /src/tests/btc_four_zeroes/rtl/top_test.v: -------------------------------------------------------------------------------- 1 | module top_test 2 | ( 3 | clk_in, 4 | reset_n_in, 5 | // Global data 6 | sck0_in, 7 | sdi0_in, 8 | cs0_n_in, 9 | // Daisy data 10 | sck1_in, 11 | sdi1_in, 12 | sdo1_out, 13 | cs1_n_in, 14 | // READY flags 15 | ready_n_od_out, 16 | // Indicator LED 17 | status_led_n_out 18 | ); 19 | 20 | // Making pool smaller to test / fit on HX8K 21 | `define SHAPOOL_NO_NONCE_OFFSET // Required for POOL_SIZE = 1 22 | 23 | parameter POOL_SIZE = 1; 24 | parameter POOL_SIZE_LOG2 = 0; 25 | parameter BASE_TARGET = 4; // Max. ~112 with 2 cores, 256 with 1 core 26 | 27 | // 12 MHz ~ 30 MHz 28 | parameter PLL_DIVR = 4'b0000; 29 | parameter PLL_DIVF = 7'b1001111; 30 | parameter PLL_DIVQ = 3'b101; 31 | 32 | // Multiply input clock signal using SB_PLL40_CORE 33 | wire g_clk; 34 | 35 | SB_PLL40_CORE #( 36 | .FEEDBACK_PATH("SIMPLE"), 37 | .DIVR(PLL_DIVR), 38 | .DIVF(PLL_DIVF), 39 | .DIVQ(PLL_DIVQ), 40 | .FILTER_RANGE(3'b001) 41 | ) 42 | pll ( 43 | .LOCK(pll_locked), 44 | .RESETB(1'b1), 45 | .BYPASS(1'b0), 46 | .REFERENCECLK(clk_in), 47 | //.PLLOUTCORE(g_clk) 48 | .PLLOUTGLOBAL(g_clk) 49 | ); 50 | 51 | // Inputs and Outputs 52 | 53 | input wire clk_in; 54 | 55 | input wire reset_n_in; 56 | 57 | input wire sck0_in; 58 | input wire sdi0_in; 59 | input wire cs0_n_in; 60 | 61 | input wire sck1_in; 62 | input wire sdi1_in; 63 | output wire sdo1_out; 64 | input wire cs1_n_in; 65 | 66 | output wire ready_n_od_out; 67 | 68 | output wire status_led_n_out; 69 | 70 | top #( 71 | .POOL_SIZE(POOL_SIZE), 72 | .POOL_SIZE_LOG2(POOL_SIZE_LOG2), 73 | .BASE_TARGET(BASE_TARGET) 74 | ) 75 | u ( 76 | g_clk, 77 | reset_n_in, 78 | // Global data 79 | sck0_in, 80 | sdi0_in, 81 | cs0_n_in, 82 | // Daisy data 83 | sck1_in, 84 | sdi1_in, 85 | sdo1_out, 86 | cs1_n_in, 87 | // Success flags 88 | ready_n_od_out, 89 | // Indicators 90 | status_led_n_out 91 | ); 92 | 93 | endmodule 94 | -------------------------------------------------------------------------------- /src/tests/btc_sixteen_zeroes/Makefile: -------------------------------------------------------------------------------- 1 | VENDOR_PATH=../vendor/ 2 | MUNIT_C_PATH=../vendor/munit/munit.c 3 | ICEPOOL_PATH=../vendor/icepool-driver/src/ 4 | LIBICEPOOL_PATH=../vendor/icepool-driver/src/ 5 | FTDI_PATH=/usr/include/ftdi1/ 6 | LIBFTDI_PATH=/usr/lib/ 7 | 8 | .phony : build 9 | build : btc_sixteen_zeroes gateware 10 | 11 | .phony : run 12 | run : build flash 13 | ./btc_sixteen_zeroes 14 | 15 | btc_sixteen_zeroes : main.c $(MUNIT_C_PATH) libicepool 16 | gcc --std=c99 -g -o $@ -I$(VENDOR_PATH) -I$(ICEPOOL_PATH) -I$(FTDI_PATH) $< $(MUNIT_C_PATH) -L$(LIBICEPOOL_PATH) -licepool-d -L$(LIBFTDI_PATH) -lftdi1 17 | 18 | .phony : libicepool 19 | libicepool : 20 | make -C $(ICEPOOL_PATH) libicepool-d.a 21 | 22 | .phony : gateware 23 | gateware : 24 | make -C ./rtl build 25 | 26 | .phony : flash 27 | flash : 28 | make -C rtl/ flash 29 | 30 | .phony : clean 31 | clean : 32 | make -C ./rtl clean 33 | rm -f ./btc_sixteen_zeroes -------------------------------------------------------------------------------- /src/tests/btc_sixteen_zeroes/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "munit/munit.h" 4 | #include "icepool.h" 5 | 6 | #define DEVICE_CONFIG_LEN (8/8) 7 | #define DEVICE_RESULT_LEN (40/8) 8 | #define JOB_CONFIG_LEN (352/8) 9 | 10 | void print_buffer(const uint8_t* buffer, size_t buffer_len) 11 | { 12 | for(size_t i = 0; i < buffer_len; ) 13 | { 14 | for (size_t j = 0; j < 8 && i < buffer_len; j++, i++) 15 | { 16 | printf("%02x ", buffer[i]); 17 | } 18 | printf("\n"); 19 | } 20 | } 21 | 22 | void test_btc_sixteen_zeroes(IcepoolContext *ctx) 23 | { 24 | // Set up device data 25 | uint8_t device_config[DEVICE_CONFIG_LEN] = { 0 }; 26 | 27 | // Set up job data 28 | uint8_t job_config[JOB_CONFIG_LEN] = { 29 | // SHA initial state: 30 | 0xdc, 0x6a, 0x3b, 0x8d, 0x0c, 0x69, 0x42, 0x1a, 31 | 0xcb, 0x1a, 0x54, 0x34, 0xe5, 0x36, 0xf7, 0xd5, 32 | 0xc3, 0xc1, 0xb9, 0xe4, 0x4c, 0xbb, 0x9b, 0x8f, 33 | 0x95, 0xf0, 0x17, 0x2e, 0xfc, 0x48, 0xd2, 0xdf, 34 | // Message head: 35 | 0xdc, 0x14, 0x17, 0x87, 0x35, 0x8b, 0x05, 0x53, 36 | 0x53, 0x5f, 0x01, 0x19, 37 | }; 38 | 39 | // Assert reset_n 40 | icepool_assert_reset(ctx); 41 | 42 | // Send device data 43 | icepool_spi_assert_daisy(ctx); 44 | 45 | icepool_spi_write_daisy(ctx, device_config, DEVICE_CONFIG_LEN); 46 | 47 | icepool_spi_deassert_daisy(ctx); 48 | 49 | // Send job data 50 | icepool_spi_assert_shared(ctx); 51 | 52 | icepool_spi_write_shared(ctx, job_config, JOB_CONFIG_LEN); 53 | 54 | icepool_spi_deassert_shared(ctx); 55 | 56 | // Deassert reset_n (start executing) 57 | icepool_deassert_reset(ctx); 58 | 59 | // Wait for READY 60 | bool ready = false; 61 | for (size_t i = 0; !ready && i < 1e9; i++) 62 | { 63 | ready = icepool_poll_ready(ctx); 64 | } 65 | 66 | munit_assert_true(ready); 67 | 68 | // Get result 69 | 70 | uint8_t expected_result[DEVICE_RESULT_LEN] = { 0x02, 0x00, 0x00, 0x40, 0xa2 }; 71 | uint8_t result[DEVICE_RESULT_LEN] = { 0xFF }; 72 | 73 | icepool_spi_assert_daisy(ctx); 74 | 75 | icepool_spi_read_daisy(ctx, result, DEVICE_RESULT_LEN); 76 | 77 | icepool_spi_assert_daisy(ctx); 78 | 79 | // Assert reset_n 80 | icepool_assert_reset(ctx); 81 | 82 | print_buffer(result, DEVICE_RESULT_LEN); 83 | 84 | munit_assert_memory_equal(DEVICE_RESULT_LEN, result, expected_result); 85 | } 86 | 87 | int main() 88 | { 89 | // Set up icepool context 90 | IcepoolContext* ctx = icepool_new(); 91 | 92 | if (!ctx) { 93 | fprintf(stderr, "Could not initialize IcepoolContext. Quitting...\n"); 94 | exit(EXIT_FAILURE); 95 | } 96 | 97 | test_btc_sixteen_zeroes(ctx); 98 | 99 | icepool_free(ctx); 100 | 101 | return EXIT_SUCCESS; 102 | } -------------------------------------------------------------------------------- /src/tests/btc_sixteen_zeroes/rtl/Makefile: -------------------------------------------------------------------------------- 1 | SHAPOOL_PATH=../../../ 2 | SHAPOOL_DEPS=$(SHAPOOL_PATH)top.v \ 3 | $(SHAPOOL_PATH)shapool.v \ 4 | $(SHAPOOL_PATH)sha_unit.v \ 5 | $(SHAPOOL_PATH)sha_round.v \ 6 | $(SHAPOOL_PATH)w_expand.v \ 7 | $(SHAPOOL_PATH)SHA256_K.v \ 8 | $(SHAPOOL_PATH)external_io.v 9 | 10 | TEST_NAME=btc_sixteen_zeroes 11 | SHAPOOL_PCF=$(SHAPOOL_PATH)pinout-icepool-ice40hx8k.pcf 12 | 13 | .phony : build 14 | build : $(TEST_NAME).bin 15 | 16 | .phony: flash 17 | flash : $(TEST_NAME).bin 18 | iceprog -v -S $^ 19 | 20 | $(TEST_NAME).bin : $(TEST_NAME).asc 21 | icepack $^ $@ 22 | 23 | $(TEST_NAME).asc : $(TEST_NAME).json $(SHAPOOL_PCF) 24 | nextpnr-ice40 --package bg121 --hx8k --json $(TEST_NAME).json --pcf $(SHAPOOL_PCF) --asc $@ 25 | 26 | $(TEST_NAME).json : ./top_test.v $(SHAPOOL_DEPS) 27 | yosys -p 'synth_ice40 -top top_test -json $@ -abc2 -retime' $^ 28 | 29 | # Clean up 30 | .phony: clean 31 | clean : 32 | rm -f $(TEST_NAME).out \ 33 | $(TEST_NAME).json \ 34 | $(TEST_NAME).asc \ 35 | $(TEST_NAME).bin -------------------------------------------------------------------------------- /src/tests/btc_sixteen_zeroes/rtl/top_test.v: -------------------------------------------------------------------------------- 1 | module top_test 2 | ( 3 | clk_in, 4 | reset_n_in, 5 | // Global data 6 | sck0_in, 7 | sdi0_in, 8 | cs0_n_in, 9 | // Daisy data 10 | sck1_in, 11 | sdi1_in, 12 | sdo1_out, 13 | cs1_n_in, 14 | // READY flags 15 | ready_n_od_out, 16 | // Indicator LED 17 | status_led_n_out 18 | ); 19 | parameter POOL_SIZE = 2; 20 | parameter POOL_SIZE_LOG2 = 1; 21 | parameter BASE_TARGET = 16; 22 | 23 | // 12 MHz ~ 30 MHz 24 | parameter PLL_DIVR = 4'b0000; 25 | parameter PLL_DIVF = 7'b1001111; 26 | parameter PLL_DIVQ = 3'b101; 27 | 28 | // Multiply input clock signal using SB_PLL40_CORE 29 | wire g_clk; 30 | 31 | SB_PLL40_CORE #( 32 | .FEEDBACK_PATH("SIMPLE"), 33 | .DIVR(PLL_DIVR), 34 | .DIVF(PLL_DIVF), 35 | .DIVQ(PLL_DIVQ), 36 | .FILTER_RANGE(3'b001) 37 | ) 38 | pll ( 39 | .LOCK(pll_locked), 40 | .RESETB(1'b1), 41 | .BYPASS(1'b0), 42 | .REFERENCECLK(clk_in), 43 | //.PLLOUTCORE(g_clk) 44 | .PLLOUTGLOBAL(g_clk) 45 | ); 46 | 47 | // Inputs and Outputs 48 | 49 | input wire clk_in; 50 | 51 | input wire reset_n_in; 52 | 53 | input wire sck0_in; 54 | input wire sdi0_in; 55 | input wire cs0_n_in; 56 | 57 | input wire sck1_in; 58 | input wire sdi1_in; 59 | output wire sdo1_out; 60 | input wire cs1_n_in; 61 | 62 | output wire ready_n_od_out; 63 | 64 | output wire status_led_n_out; 65 | 66 | top #( 67 | .POOL_SIZE(POOL_SIZE), 68 | .POOL_SIZE_LOG2(POOL_SIZE_LOG2), 69 | .BASE_TARGET(BASE_TARGET) 70 | ) 71 | u ( 72 | g_clk, 73 | reset_n_in, 74 | // Global data 75 | sck0_in, 76 | sdi0_in, 77 | cs0_n_in, 78 | // Daisy data 79 | sck1_in, 80 | sdi1_in, 81 | sdo1_out, 82 | cs1_n_in, 83 | // Success flags 84 | ready_n_od_out, 85 | // Indicators 86 | status_led_n_out 87 | ); 88 | 89 | endmodule 90 | -------------------------------------------------------------------------------- /src/tests/external_io_usage_tb.v: -------------------------------------------------------------------------------- 1 | `timescale 10ns/100ps 2 | 3 | module external_io_usage_tb(); 4 | 5 | /* 6 | Tests basic usage of `external_io` module. 7 | */ 8 | 9 | `define VERILATOR 10 | `define DEBUG_VERBOSE 11 | 12 | localparam spi_bit_half_period = 6; // 3 clock cycles 13 | localparam reset_hold_period = 2; // 1 clock cycle 14 | 15 | localparam JOB_CONFIG_WIDTH = 8; 16 | localparam DEVICE_CONFIG_WIDTH = 8; 17 | 18 | reg clk; 19 | reg reset_n; 20 | // SPI(0) 21 | reg sck0; 22 | reg sdi0; 23 | reg cs0_n; 24 | // SPI(1) 25 | reg sck1; 26 | reg sdi1; 27 | wire sdo1; 28 | reg cs1_n; 29 | // Stored data 30 | wire [DEVICE_CONFIG_WIDTH-1:0] device_config; 31 | wire [JOB_CONFIG_WIDTH-1:0] job_config; 32 | // Control flag 33 | wire core_reset_n; 34 | // From shapool 35 | reg [7:0] shapool_match_flags; 36 | reg [31:0] shapool_result; 37 | reg shapool_success; 38 | // READY signal 39 | wire ready; 40 | 41 | external_io 42 | #(.JOB_CONFIG_WIDTH(DEVICE_CONFIG_WIDTH), 43 | .DEVICE_CONFIG_WIDTH(JOB_CONFIG_WIDTH)) 44 | uut ( 45 | clk, 46 | reset_n, 47 | // SPI(0) 48 | sck0, 49 | sdi0, 50 | cs0_n, 51 | // SPI(1) 52 | sck1, 53 | sdi1, 54 | sdo1, 55 | cs1_n, 56 | // Stored data 57 | device_config, 58 | job_config, 59 | // Control flags 60 | core_reset_n, 61 | // From shapool 62 | shapool_success, 63 | { shapool_match_flags, shapool_result}, 64 | // READY signal 65 | ready 66 | ); 67 | 68 | reg [31:0] i; 69 | 70 | localparam [39:0] expected_result = 40'hAA_EEDDCCBB; 71 | localparam [DEVICE_CONFIG_WIDTH-1:0] expected_device_config = 8'b10101010; 72 | localparam [JOB_CONFIG_WIDTH-1:0] expected_job_config = 8'b10101010; 73 | 74 | reg [JOB_CONFIG_WIDTH-1:0] test_job_config = expected_job_config; 75 | reg [DEVICE_CONFIG_WIDTH-1:0] test_device_config = expected_device_config; 76 | reg [39:0] test_result = 0; 77 | 78 | // Generate clock 79 | always 80 | begin 81 | clk <= 0; 82 | #1; 83 | clk <= 1; 84 | #1; 85 | end 86 | 87 | initial 88 | begin 89 | 90 | $dumpfile("external_io_usage_tb.vcd"); 91 | $dumpvars; 92 | 93 | // Initial states 94 | reset_n <= 0; 95 | sck0 <= 0; 96 | sdi0 <= 0; 97 | cs0_n <= 1; 98 | 99 | sck1 <= 0; 100 | sdi1 <= 0; 101 | cs1_n <= 1; 102 | shapool_result <= 32'hEEDDCCBB; 103 | shapool_match_flags <= 8'hAA; 104 | shapool_success <= 0; 105 | 106 | #10; 107 | 108 | //////////////////////////////////////////// 109 | // Test SPI0 (clock in job configuration) // 110 | //////////////////////////////////////////// 111 | 112 | if (core_reset_n == 0) 113 | begin 114 | $display("\033\133\063\062\155[PASS]\033\133\060\155 `external_io`: core_reset_n"); 115 | end 116 | else 117 | begin 118 | $display("\033\133\063\061\155[FAIL]\033\133\060\155 `external_io`: core_reset_n"); 119 | $error("Test case failed: core_reset_n should be low after reset_n goes low and reset_hold_period elapsed."); 120 | end 121 | 122 | // (SPI Mode 0,0) 123 | cs0_n <= 0; 124 | #reset_hold_period; 125 | 126 | for (i = 0; i < JOB_CONFIG_WIDTH; i = i + 1) 127 | begin 128 | // Data out before rising edge 129 | sdi0 <= test_job_config[JOB_CONFIG_WIDTH-1]; 130 | test_job_config <= { test_job_config[JOB_CONFIG_WIDTH-2:0], 1'b0 }; 131 | #spi_bit_half_period; 132 | 133 | // Rising edge (uut samples) 134 | sck0 <= 1; 135 | #spi_bit_half_period; 136 | 137 | // Falling edge (next bit out) 138 | sck0 <= 0; 139 | end 140 | 141 | cs0_n <= 1; 142 | #10; 143 | 144 | if (uut.job_config == expected_job_config) 145 | begin 146 | $display("\033\133\063\062\155[PASS]\033\133\060\155 `external_io`: shift in job configuration"); 147 | end 148 | else 149 | begin 150 | $display("\033\133\063\061\155[FAIL]\033\133\060\155 `external_io`: shift in job configuration"); 151 | $display("uut.job_config: %h", uut.job_config); 152 | $error("Test case failed: SPI0 did not properly load uut.job_config."); 153 | end 154 | 155 | /////////////////////////////////////////////// 156 | // Test SPI1 (clock in device configuration) // 157 | /////////////////////////////////////////////// 158 | 159 | // (SPI Mode 0,0) 160 | cs1_n <= 0; 161 | 162 | for (i = 0; i < DEVICE_CONFIG_WIDTH; i = i + 1) 163 | begin 164 | // Data out before rising edge 165 | sdi1 <= test_device_config[DEVICE_CONFIG_WIDTH-1]; 166 | test_device_config <= { test_device_config[DEVICE_CONFIG_WIDTH-2:0], 1'b0 }; 167 | #spi_bit_half_period; 168 | 169 | // Rising edge (uut samples) 170 | sck1 <= 1; 171 | #spi_bit_half_period; 172 | 173 | // Falling edge (next bit out) 174 | sck1 <= 0; 175 | end 176 | 177 | cs1_n <= 1; 178 | reset_n <= 1; 179 | 180 | if (uut.device_config == expected_device_config) 181 | begin 182 | $display("\033\133\063\062\155[PASS]\033\133\060\155 `external_io`: shift in device configuration"); 183 | end 184 | else 185 | begin 186 | $display("\033\133\063\061\155[FAIL]\033\133\060\155 `external_io`: shift in device configuration"); 187 | $display("uut.device_config: %h", uut.device_config); 188 | $error("Test case failed: SPI1 did not properly load uut.device_config"); 189 | end 190 | 191 | // TODO test shifting out sdo1 192 | 193 | ///////////////////////////////////////////// 194 | // Test SPI1 on success (clock out result) // 195 | ///////////////////////////////////////////// 196 | 197 | shapool_success <= 1; 198 | 199 | // (SPI Mode 0,0) 200 | cs1_n <= 0; 201 | sdi1 <= 0; 202 | 203 | for (i = 0; i < 40; i = i + 1) 204 | begin 205 | // data out 206 | #spi_bit_half_period; 207 | 208 | // Rising edge (sample) 209 | test_result <= { test_result[38:0], sdo1 }; 210 | sck1 <= 1; 211 | #spi_bit_half_period; 212 | 213 | // Falling edge (next bit out) 214 | sck1 <= 0; 215 | end 216 | 217 | cs1_n <= 1; 218 | #reset_hold_period; 219 | 220 | if (test_result == expected_result) 221 | begin 222 | $display("\033\133\063\062\155[PASS]\033\133\060\155 `external_io`: shift out result"); 223 | end 224 | else 225 | begin 226 | $display("\033\133\063\061\155[FAIL]\033\133\060\155 `external_io`: shift out result"); 227 | $display("test_result: %h", test_result); 228 | $error("Test case failed: SPI1 failed to shift out result properly."); 229 | end 230 | 231 | #100; 232 | $finish; 233 | end 234 | 235 | endmodule -------------------------------------------------------------------------------- /src/tests/ram_init_test/Makefile: -------------------------------------------------------------------------------- 1 | ICEPOOL_PATH=../vendor/icepool-driver/src/ 2 | LIBICEPOOL_PATH=../vendor/icepool-driver/src/ 3 | FTDI_PATH=/usr/include/ftdi1/ 4 | LIBFTDI_PATH=/usr/lib/ 5 | 6 | .phony : build 7 | build : ram_init_test gateware 8 | 9 | .phony : run 10 | run : build flash 11 | ./ram_init_test 12 | 13 | ram_init_test : main.c libicepool 14 | gcc --std=c99 -g -o $@ -I$(ICEPOOL_PATH) -I$(FTDI_PATH) $< -L$(LIBICEPOOL_PATH) -licepool-d -L$(LIBFTDI_PATH) -lftdi1 15 | 16 | .phony : libicepool 17 | libicepool : 18 | make -C $(ICEPOOL_PATH) libicepool-d.a 19 | 20 | .phony : gateware 21 | gateware : 22 | make -C ./rtl build 23 | 24 | .phony : flash 25 | flash : 26 | make -C rtl/ flash 27 | 28 | .phony : clean 29 | clean : 30 | make -C ./rtl clean 31 | rm -f ./ram_init_test -------------------------------------------------------------------------------- /src/tests/ram_init_test/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "icepool.h" 4 | 5 | #define DEVICE_COUNT 1 6 | #define DUMP_SIZE (64/8) 7 | 8 | void print_buffer(const uint8_t* buffer, size_t buffer_len) 9 | { 10 | for(size_t i = 0; i < buffer_len; ) 11 | { 12 | for (size_t j = 0; j < 8 && i < buffer_len; j++, i++) 13 | { 14 | printf("%02x ", buffer[i]); 15 | } 16 | printf("\n"); 17 | } 18 | } 19 | 20 | void dump_Kt(IcepoolContext* ctx) 21 | { 22 | uint8_t dump[DUMP_SIZE] = { 0 }; 23 | 24 | icepool_assert_reset(ctx); 25 | icepool_deassert_reset(ctx); 26 | 27 | // Wait for READY 28 | while(icepool_poll_ready(ctx) == false) { printf("."); fflush(stdout); } 29 | printf("\n"); 30 | 31 | icepool_spi_assert_daisy(ctx); 32 | 33 | icepool_spi_read_daisy(ctx, dump, DUMP_SIZE); 34 | 35 | icepool_spi_assert_daisy(ctx); 36 | 37 | icepool_assert_reset(ctx); 38 | 39 | printf("0x10_000000\t"); 40 | print_buffer(&dump[0], 4); 41 | 42 | printf("0x10_111111\t"); 43 | print_buffer(&dump[4], 4); 44 | } 45 | 46 | int main() 47 | { 48 | // Set up icepool context 49 | IcepoolContext* ctx = icepool_new(); 50 | 51 | if (!ctx) { 52 | fprintf(stderr, "Could not initialize IcepoolContext. Quitting...\n"); 53 | exit(EXIT_FAILURE); 54 | } 55 | 56 | dump_Kt(ctx); 57 | 58 | icepool_free(ctx); 59 | 60 | return EXIT_SUCCESS; 61 | } -------------------------------------------------------------------------------- /src/tests/ram_init_test/rtl/Makefile: -------------------------------------------------------------------------------- 1 | SHAPOOL_PATH=../../../ 2 | SHAPOOL_DEPS=$(SHAPOOL_PATH)external_io.v 3 | 4 | TEST_NAME=ram_init_test 5 | SHAPOOL_PCF=$(SHAPOOL_PATH)pinout-icepool-ice40hx8k.pcf 6 | 7 | .phony : build 8 | build : $(TEST_NAME).bin 9 | 10 | .phony: flash 11 | flash : $(TEST_NAME).bin 12 | iceprog -v -S $^ 13 | 14 | $(TEST_NAME).bin : $(TEST_NAME).asc 15 | icepack $^ $@ 16 | 17 | $(TEST_NAME).asc : $(TEST_NAME).json $(SHAPOOL_PCF) 18 | nextpnr-ice40 --package bg121 --hx8k --json $(TEST_NAME).json --pcf $(SHAPOOL_PCF) --asc $@ 19 | 20 | $(TEST_NAME).json : ./top_test.v $(SHAPOOL_DEPS) 21 | yosys -p 'synth_ice40 -top top_test -json $@ -abc2 -retime' $^ 22 | 23 | # Clean up 24 | .phony: clean 25 | clean : 26 | rm -f $(TEST_NAME).out \ 27 | $(TEST_NAME).json \ 28 | $(TEST_NAME).asc \ 29 | $(TEST_NAME).bin -------------------------------------------------------------------------------- /src/tests/ram_init_test/rtl/top_test.v: -------------------------------------------------------------------------------- 1 | module top_test 2 | ( 3 | clk_in, 4 | reset_n_in, 5 | // Global data 6 | sck0_in, 7 | sdi0_in, 8 | cs0_n_in, 9 | // Daisy data 10 | sck1_in, 11 | sdi1_in, 12 | sdo1_out, 13 | cs1_n_in, 14 | // READY flags 15 | ready_n_od_out, 16 | // Indicator LED 17 | status_led_n_out 18 | ); 19 | // 12 MHz ~ 48 MHz 20 | parameter PLL_DIVR = 4'b0000; 21 | parameter PLL_DIVF = 7'b1111111; 22 | parameter PLL_DIVQ = 3'b100; 23 | 24 | // 12 MHz ~ 56.25 MHz 25 | // parameter PLL_DIVR = 4'b0000; 26 | // parameter PLL_DIVF = 7'b1001010; 27 | // parameter PLL_DIVQ = 3'b100; 28 | 29 | // Multiply input clock signal using SB_PLL40_CORE 30 | wire g_clk; 31 | 32 | SB_PLL40_CORE #( 33 | .FEEDBACK_PATH("SIMPLE"), 34 | .DIVR(PLL_DIVR), 35 | .DIVF(PLL_DIVF), 36 | .DIVQ(PLL_DIVQ), 37 | .FILTER_RANGE(3'b001) 38 | ) 39 | pll ( 40 | .LOCK(pll_locked), 41 | .RESETB(1'b1), 42 | .BYPASS(1'b0), 43 | .REFERENCECLK(clk_in), 44 | //.PLLOUTCORE(g_clk) 45 | .PLLOUTGLOBAL(g_clk) 46 | ); 47 | 48 | // Inputs and Outputs 49 | 50 | input wire clk_in; 51 | 52 | input wire reset_n_in; 53 | 54 | input wire sck0_in; 55 | input wire sdi0_in; 56 | input wire cs0_n_in; 57 | 58 | input wire sck1_in; 59 | input wire sdi1_in; 60 | output wire sdo1_out; 61 | input wire cs1_n_in; 62 | 63 | output wire ready_n_od_out; 64 | 65 | output wire status_led_n_out; 66 | 67 | // Set up dummy external_io 68 | 69 | external_io #( 70 | .POOL_SIZE(1), 71 | .POOL_SIZE_LOG2(0), 72 | .JOB_CONFIG_WIDTH(8), // smallest 73 | .DEVICE_CONFIG_WIDTH(8), // smallest 74 | .RESULT_DATA_WIDTH(64) 75 | ) 76 | io ( 77 | .clk(g_clk), 78 | .reset_n(reset_n_in), 79 | // SPI(0) 80 | .sck0(sck0_in), 81 | .sdi0(sdi0_in), 82 | .cs0_n(cs0_n_in), 83 | // SPI(1) 84 | .sck1(sck1_in), 85 | .sdi1(sdi1_in), 86 | .sdo1(sdo1_out), 87 | .cs1_n(cs1_n_in), 88 | // Stored data 89 | .device_config(), 90 | .job_config(), 91 | // Control signals 92 | .core_reset_n(), 93 | // From shapool 94 | .shapool_success(done), 95 | .shapool_result(dump), 96 | // READY signal 97 | .ready() 98 | ); 99 | 100 | // Read all RAM values into register 101 | 102 | reg [63:0] dump; 103 | reg [5:0] round = 0; 104 | 105 | reg done = 0; 106 | 107 | always @(posedge g_clk) 108 | begin 109 | if (!reset_n_in) 110 | begin 111 | round <= 0; 112 | done <= 0; 113 | end 114 | else if (done == 0) 115 | begin 116 | 117 | round <= round + 1; 118 | 119 | if (round == 0) 120 | begin 121 | dump <= { Kt, dump[63:32] }; 122 | end 123 | 124 | if (round == 63) 125 | begin 126 | dump <= { Kt, dump[63:32] }; 127 | done <= 1; 128 | end 129 | 130 | end 131 | end 132 | 133 | assign ready_n_od_out = (done == 1) ? 1'b0 : 1'bz; 134 | assign status_led_n_out = !done; 135 | 136 | // RAM setup 137 | wire [15:0] data_hi; 138 | wire [15:0] data_lo; 139 | 140 | wire [31:0] Kt; 141 | assign Kt = { data_hi, data_lo }; 142 | 143 | wire [7:0] address; 144 | assign address = { 2'b10, round }; 145 | 146 | SB_RAM40_4K ram256x16_hi ( 147 | .RDATA(data_hi), 148 | .RADDR(address), 149 | .RCLK(g_clk), 150 | .RCLKE(1), 151 | .RE(1), 152 | .WADDR(0), 153 | .WCLK(0), 154 | .WCLKE(0), 155 | .WDATA(0), 156 | .WE(0), 157 | .MASK(0) 158 | ); 159 | 160 | SB_RAM40_4K ram256x16_lo ( 161 | .RDATA(data_lo), 162 | .RADDR(address), 163 | .RCLK(g_clk), 164 | .RCLKE(1), 165 | .RE(1), 166 | .WADDR(0), 167 | .WCLK(0), 168 | .WCLKE(0), 169 | .WDATA(0), 170 | .WE(0), 171 | .MASK(0) 172 | ); 173 | 174 | defparam ram256x16_hi.READ_MODE = 0; // 0 = 256x16 175 | defparam ram256x16_hi.WRITE_MODE = 0; // 0 = 256x16 176 | 177 | defparam ram256x16_lo.READ_MODE = 0; // 0 = 256x16 178 | defparam ram256x16_lo.WRITE_MODE = 0; // 0 = 256x16 179 | 180 | defparam ram256x16_hi.INIT_0 = 256'h0000_0001_0002_0003_0004_0005_0006_0007_0008_0009_000a_000b_000c_000d_000e_000f; 181 | defparam ram256x16_hi.INIT_1 = 256'h0010_0011_0012_0013_0014_0015_0016_0017_0018_0019_001a_001b_001c_001d_001e_001f; 182 | defparam ram256x16_hi.INIT_2 = 256'h0020_0021_0022_0023_0024_0025_0026_0027_0028_0029_002a_002b_002c_002d_002e_002f; 183 | defparam ram256x16_hi.INIT_3 = 256'h0030_0031_0032_0033_0034_0035_0036_0037_0038_0039_003a_003b_003c_003d_003e_003f; 184 | defparam ram256x16_hi.INIT_4 = 256'h0040_0041_0042_0043_0044_0045_0046_0047_0048_0049_004a_004b_004c_004d_004e_004f; 185 | defparam ram256x16_hi.INIT_5 = 256'h0050_0051_0052_0053_0054_0055_0056_0057_0058_0059_005a_005b_005c_005d_005e_005f; 186 | defparam ram256x16_hi.INIT_6 = 256'h0060_0061_0062_0063_0064_0065_0066_0067_0068_0069_006a_006b_006c_006d_006e_006f; 187 | defparam ram256x16_hi.INIT_7 = 256'h0070_0071_0072_0073_0074_0075_0076_0077_0078_0079_007a_007b_007c_007d_007e_007f; 188 | defparam ram256x16_hi.INIT_8 = 256'h0080_0081_0082_0083_0084_0085_0086_0087_0088_0089_008a_008b_008c_008d_008e_008f; 189 | defparam ram256x16_hi.INIT_9 = 256'h0090_0091_0092_0093_0094_0095_0096_0097_0098_0099_009a_009b_009c_009d_009e_009f; 190 | defparam ram256x16_hi.INIT_A = 256'h00a0_00a1_00a2_00a3_00a4_00a5_00a6_00a7_00a8_00a9_00aa_00ab_00ac_00ad_00ae_00af; 191 | defparam ram256x16_hi.INIT_B = 256'h00b0_00b1_00b2_00b3_00b4_00b5_00b6_00b7_00b8_00b9_00ba_00bb_00bc_00bd_00be_00bf; 192 | defparam ram256x16_hi.INIT_C = 256'h00c0_00c1_00c2_00c3_00c4_00c5_00c6_00c7_00c8_00c9_00ca_00cb_00cc_00cd_00ce_00cf; 193 | defparam ram256x16_hi.INIT_D = 256'h00d0_00d1_00d2_00d3_00d4_00d5_00d6_00d7_00d8_00d9_00da_00db_00dc_00dd_00de_00df; 194 | defparam ram256x16_hi.INIT_E = 256'h00e0_00e1_00e2_00e3_00e4_00e5_00e6_00e7_00e8_00e9_00ea_00eb_00ec_00ed_00ee_00ef; 195 | defparam ram256x16_hi.INIT_F = 256'h00f0_00f1_00f2_00f3_00f4_00f5_00f6_00f7_00f8_00f9_00fa_00fb_00fc_00fd_00fe_00ff; 196 | 197 | defparam ram256x16_lo.INIT_0 = 256'h0100_0101_0102_0103_0104_0105_0106_0107_0108_0109_010a_010b_010c_010d_010e_010f; 198 | defparam ram256x16_lo.INIT_1 = 256'h0110_0111_0112_0113_0114_0115_0116_0117_0118_0119_011a_011b_011c_011d_011e_011f; 199 | defparam ram256x16_lo.INIT_2 = 256'h0120_0121_0122_0123_0124_0125_0126_0127_0128_0129_012a_012b_012c_012d_012e_012f; 200 | defparam ram256x16_lo.INIT_3 = 256'h0130_0131_0132_0133_0134_0135_0136_0137_0138_0139_013a_013b_013c_013d_013e_013f; 201 | defparam ram256x16_lo.INIT_4 = 256'h0140_0141_0142_0143_0144_0145_0146_0147_0148_0149_014a_014b_014c_014d_014e_014f; 202 | defparam ram256x16_lo.INIT_5 = 256'h0150_0151_0152_0153_0154_0155_0156_0157_0158_0159_015a_015b_015c_015d_015e_015f; 203 | defparam ram256x16_lo.INIT_6 = 256'h0160_0161_0162_0163_0164_0165_0166_0167_0168_0169_016a_016b_016c_016d_016e_016f; 204 | defparam ram256x16_lo.INIT_7 = 256'h0170_0171_0172_0173_0174_0175_0176_0177_0178_0179_017a_017b_017c_017d_017e_017f; 205 | defparam ram256x16_lo.INIT_8 = 256'h0180_0181_0182_0183_0184_0185_0186_0187_0188_0189_018a_018b_018c_018d_018e_018f; 206 | defparam ram256x16_lo.INIT_9 = 256'h0190_0191_0192_0193_0194_0195_0196_0197_0198_0199_019a_019b_019c_019d_019e_019f; 207 | defparam ram256x16_lo.INIT_A = 256'h01a0_01a1_01a2_01a3_01a4_01a5_01a6_01a7_01a8_01a9_01aa_01ab_01ac_01ad_01ae_01af; 208 | defparam ram256x16_lo.INIT_B = 256'h01b0_01b1_01b2_01b3_01b4_01b5_01b6_01b7_01b8_01b9_01ba_01bb_01bc_01bd_01be_01bf; 209 | defparam ram256x16_lo.INIT_C = 256'h01c0_01c1_01c2_01c3_01c4_01c5_01c6_01c7_01c8_01c9_01ca_01cb_01cc_01cd_01ce_01cf; 210 | defparam ram256x16_lo.INIT_D = 256'h01d0_01d1_01d2_01d3_01d4_01d5_01d6_01d7_01d8_01d9_01da_01db_01dc_01dd_01de_01df; 211 | defparam ram256x16_lo.INIT_E = 256'h01e0_01e1_01e2_01e3_01e4_01e5_01e6_01e7_01e8_01e9_01ea_01eb_01ec_01ed_01ee_01ef; 212 | defparam ram256x16_lo.INIT_F = 256'h01f0_01f1_01f2_01f3_01f4_01f5_01f6_01f7_01f8_01f9_01fa_01fb_01fc_01fd_01fe_01ff; 213 | 214 | endmodule 215 | -------------------------------------------------------------------------------- /src/tests/sha_round_usage_tb.v: -------------------------------------------------------------------------------- 1 | `timescale 10ns/100ps 2 | 3 | module sha_round_usage_tb(); 4 | 5 | /* 6 | Tests basic usage of `sha_round`. 7 | 8 | * `sha_round` is a concurrent module. 9 | * Testing that the output is correct for known inputs. 10 | */ 11 | 12 | `define VERILATOR 13 | `define DEBUG_VERBOSE 14 | 15 | localparam NUMBER_OF_CASES = 3; 16 | reg [255:0] S0_case [0:NUMBER_OF_CASES-1]; 17 | reg [31:0] Kt_case [0:NUMBER_OF_CASES-1]; 18 | reg [31:0] Wt_case [0:NUMBER_OF_CASES-1]; 19 | reg [255:0] S1_expected [0:NUMBER_OF_CASES-1]; 20 | 21 | // Set up case data 22 | 23 | // Case 0 24 | initial S0_case[0] = 256'h6a09e667_bb67ae85_3c6ef372_a54ff53a_510e527f_9b05688c_1f83d9ab_5be0cd19; 25 | initial Kt_case[0] = 32'h428a2f98; 26 | initial Wt_case[0] = 32'h61626380; 27 | initial S1_expected[0] = 256'h5d6aebcd_6a09e667_bb67ae85_3c6ef372_fa2a4622_510e527f_9b05688c_1f83d9ab; 28 | 29 | // Case 1 30 | initial S0_case[1] = 256'h04d24d6c_b85e2ce9_b6ae8fff_ffb70472_948d25b6_961f4894_b21bad3d_6d83bfc6; 31 | initial Kt_case[1] = 32'hbef9a3f7; 32 | initial Wt_case[1] = 32'heeaba2cc; 33 | initial S1_expected[1] = 256'hd39a2165_04d24d6c_b85e2ce9_b6ae8fff_fb121210_948d25b6_961f4894_b21bad3d; 34 | 35 | // Case 2 36 | initial S0_case[2] = 256'b0; 37 | initial Kt_case[2] = 256'b0; 38 | initial Wt_case[2] = 256'b0; 39 | initial S1_expected[2] = 256'b0; 40 | 41 | reg [255:0] S0; 42 | reg [31:0] Kt; 43 | reg [31:0] Wt; 44 | wire [255:0] S1; 45 | 46 | sha_round uut ( 47 | .in(S0), 48 | .Kt(Kt), 49 | .Wt(Wt), 50 | .out(S1) 51 | ); 52 | 53 | reg [1:0] i = 0; 54 | 55 | initial 56 | begin 57 | for (i = 0; i < NUMBER_OF_CASES; i = i + 1) 58 | begin 59 | S0 <= S0_case[i]; 60 | Kt <= Kt_case[i]; 61 | Wt <= Wt_case[i]; 62 | #1; 63 | 64 | if (S1 == S1_expected[i]) 65 | begin 66 | $display("\033\133\063\062\155[PASS]\033\133\060\155 `sha_round`: case %0d", i); 67 | end 68 | else 69 | begin 70 | $display("\033\133\063\061\155[FAIL]\033\133\060\155 `sha_round`: case %0d", i); 71 | $display(" S0 = %h", S0[255:128]); 72 | $display(" %h", S0[127: 0]); 73 | $display(" Kt = %h", Kt); 74 | $display(" Wt = %h", Wt); 75 | $display(""); 76 | $display(" S1 (actual) = %h", S1[255:128]); 77 | $display(" %h", S1[127: 0]); 78 | $display(" S1 (expected) = %h", S1_expected[i][255:128]); 79 | $display(" %h", S1_expected[i][127: 0]); 80 | $display(""); 81 | 82 | $error("Test case failed."); 83 | end 84 | 85 | end 86 | 87 | $finish; 88 | end 89 | 90 | endmodule 91 | -------------------------------------------------------------------------------- /src/tests/sha_unit_btc_tb.v: -------------------------------------------------------------------------------- 1 | module sha_unit_usage_tb(); 2 | 3 | /* Tests basic usage of `sha_unit`. 4 | */ 5 | 6 | `define VERILATOR 7 | `define DEBUG_VERBOSE 8 | 9 | `define idx32(x) (32*((x)+1)-1):(32*(x)); 10 | 11 | // SHA256 constants 12 | reg [255:0] SHA256_H0 = { 32'h6a09e667, 32'hbb67ae85, 13 | 32'h3c6ef372, 32'ha54ff53a, 14 | 32'h510e527f, 32'h9b05688c, 15 | 32'h1f83d9ab, 32'h5be0cd19 }; 16 | 17 | localparam[2047:0] SHA256_K = 18 | { 32'h428a2f98, 32'h71374491, 32'hb5c0fbcf, 32'he9b5dba5, 19 | 32'h3956c25b, 32'h59f111f1, 32'h923f82a4, 32'hab1c5ed5, 20 | 32'hd807aa98, 32'h12835b01, 32'h243185be, 32'h550c7dc3, 21 | 32'h72be5d74, 32'h80deb1fe, 32'h9bdc06a7, 32'hc19bf174, 22 | 32'he49b69c1, 32'hefbe4786, 32'h0fc19dc6, 32'h240ca1cc, 23 | 32'h2de92c6f, 32'h4a7484aa, 32'h5cb0a9dc, 32'h76f988da, 24 | 32'h983e5152, 32'ha831c66d, 32'hb00327c8, 32'hbf597fc7, 25 | 32'hc6e00bf3, 32'hd5a79147, 32'h06ca6351, 32'h14292967, 26 | 32'h27b70a85, 32'h2e1b2138, 32'h4d2c6dfc, 32'h53380d13, 27 | 32'h650a7354, 32'h766a0abb, 32'h81c2c92e, 32'h92722c85, 28 | 32'ha2bfe8a1, 32'ha81a664b, 32'hc24b8b70, 32'hc76c51a3, 29 | 32'hd192e819, 32'hd6990624, 32'hf40e3585, 32'h106aa070, 30 | 32'h19a4c116, 32'h1e376c08, 32'h2748774c, 32'h34b0bcb5, 31 | 32'h391c0cb3, 32'h4ed8aa4a, 32'h5b9cca4f, 32'h682e6ff3, 32 | 32'h748f82ee, 32'h78a5636f, 32'h84c87814, 32'h8cc70208, 33 | 32'h90befffa, 32'ha4506ceb, 32'hbef9a3f7, 32'hc67178f2 }; 34 | 35 | wire [31:0] K[0:63]; 36 | 37 | assign K[ 0] = SHA256_K[2047:2016]; 38 | assign K[ 1] = SHA256_K[2015:1984]; 39 | assign K[ 2] = SHA256_K[1983:1952]; 40 | assign K[ 3] = SHA256_K[1951:1920]; 41 | assign K[ 4] = SHA256_K[1919:1888]; 42 | assign K[ 5] = SHA256_K[1887:1856]; 43 | assign K[ 6] = SHA256_K[1855:1824]; 44 | assign K[ 7] = SHA256_K[1823:1792]; 45 | assign K[ 8] = SHA256_K[1791:1760]; 46 | assign K[ 9] = SHA256_K[1759:1728]; 47 | assign K[10] = SHA256_K[1727:1696]; 48 | assign K[11] = SHA256_K[1695:1664]; 49 | assign K[12] = SHA256_K[1663:1632]; 50 | assign K[13] = SHA256_K[1631:1600]; 51 | assign K[14] = SHA256_K[1599:1568]; 52 | assign K[15] = SHA256_K[1567:1536]; 53 | assign K[16] = SHA256_K[1535:1504]; 54 | assign K[17] = SHA256_K[1503:1472]; 55 | assign K[18] = SHA256_K[1471:1440]; 56 | assign K[19] = SHA256_K[1439:1408]; 57 | assign K[20] = SHA256_K[1407:1376]; 58 | assign K[21] = SHA256_K[1375:1344]; 59 | assign K[22] = SHA256_K[1343:1312]; 60 | assign K[23] = SHA256_K[1311:1280]; 61 | assign K[24] = SHA256_K[1279:1248]; 62 | assign K[25] = SHA256_K[1247:1216]; 63 | assign K[26] = SHA256_K[1215:1184]; 64 | assign K[27] = SHA256_K[1183:1152]; 65 | assign K[28] = SHA256_K[1151:1120]; 66 | assign K[29] = SHA256_K[1119:1088]; 67 | assign K[30] = SHA256_K[1087:1056]; 68 | assign K[31] = SHA256_K[1055:1024]; 69 | assign K[32] = SHA256_K[1023: 992]; 70 | assign K[33] = SHA256_K[ 991: 960]; 71 | assign K[34] = SHA256_K[ 959: 928]; 72 | assign K[35] = SHA256_K[ 927: 896]; 73 | assign K[36] = SHA256_K[ 895: 864]; 74 | assign K[37] = SHA256_K[ 863: 832]; 75 | assign K[38] = SHA256_K[ 831: 800]; 76 | assign K[39] = SHA256_K[ 799: 768]; 77 | assign K[40] = SHA256_K[ 767: 736]; 78 | assign K[41] = SHA256_K[ 735: 704]; 79 | assign K[42] = SHA256_K[ 703: 672]; 80 | assign K[43] = SHA256_K[ 671: 640]; 81 | assign K[44] = SHA256_K[ 639: 608]; 82 | assign K[45] = SHA256_K[ 607: 576]; 83 | assign K[46] = SHA256_K[ 575: 544]; 84 | assign K[47] = SHA256_K[ 543: 512]; 85 | assign K[48] = SHA256_K[ 511: 480]; 86 | assign K[49] = SHA256_K[ 479: 448]; 87 | assign K[50] = SHA256_K[ 447: 416]; 88 | assign K[51] = SHA256_K[ 415: 384]; 89 | assign K[52] = SHA256_K[ 383: 352]; 90 | assign K[53] = SHA256_K[ 351: 320]; 91 | assign K[54] = SHA256_K[ 319: 288]; 92 | assign K[55] = SHA256_K[ 287: 256]; 93 | assign K[56] = SHA256_K[ 255: 224]; 94 | assign K[57] = SHA256_K[ 223: 192]; 95 | assign K[58] = SHA256_K[ 191: 160]; 96 | assign K[59] = SHA256_K[ 159: 128]; 97 | assign K[60] = SHA256_K[ 127: 96]; 98 | assign K[61] = SHA256_K[ 95: 64]; 99 | assign K[62] = SHA256_K[ 63: 32]; 100 | assign K[63] = SHA256_K[ 31: 0]; 101 | 102 | // Test case #1 - FIPS single-block "abc" 103 | reg [511:0] M_fips1 = 104 | { 128'h61626380_00000000_00000000_00000000, 105 | 128'h00000000_00000000_00000000_00000000, 106 | 128'h00000000_00000000_00000000_00000000, 107 | 128'h00000000_00000000_00000000_00000018 }; 108 | 109 | reg[255:0] H_fips1 = 110 | { 128'hba7816bf_8f01cfea_414140de_5dae2223, 111 | 128'hb00361a3_96177a9c_b410ff61_f20015ad }; 112 | 113 | // Test case #2 - FIPS multi-block "abcdbcdecdefdefgefgh..." 114 | /* 115 | reg [511:0] M_fips2 [0:1]; 116 | 117 | initial M_fips2[0] = 118 | { 128'h61626364_62636465_63646566_64656667, 119 | 128'h65666768_66676869_6768696a_68696a6b, 120 | 128'h696a6b6c_6a6b6c6d_6b6c6d6e_6c6d6e6f, 121 | 128'h6d6e6f70_6e6f7071_80000000_00000000 }; 122 | 123 | initial M_fips2[1] = 124 | { 128'h00000000_00000000_00000000_00000000, 125 | 128'h00000000_00000000_00000000_00000000, 126 | 128'h00000000_00000000_00000000_00000000, 127 | 128'h00000000_00000000_00000000_000001c0 }; 128 | 129 | reg [255:0] H_fips2[0:1]; 130 | 131 | initial H_fips2[0] = 132 | { 128'h85e655d6_417a1795_3363376a_624cde5c, 133 | 128'h76e09589_cac5f811_cc4b32c1_f20e533a }; 134 | 135 | initial H_fips2[1] = 136 | { 128'h248d6a61_d20638b8_e5c02693_0c3e6039, 137 | 128'ha33ce459_64ff2167_f6ecedd4_19db06c1 }; 138 | */ 139 | 140 | // Test case #3 - BTC example 141 | 142 | reg [511:0] M_btc[0:1]; 143 | 144 | initial M_btc[0] = 145 | { 128'h02000000_17975b97_c18ed1f7_e255adf2, // Start of data 146 | 128'h97599b55_330edab8_7803c817_01000000, // ... 147 | 128'h00000000_8a97295a_2747b4f1_a0b3948d, // ... 148 | 128'hf3990344_c0e19fa6_b2b92b3a_19c8e6ba }; // ... 149 | 150 | initial M_btc[1] = 151 | { 96'hdc141787358b0553535f0119, 32'h00000000, // end of data, nonce 152 | 128'h80000000_00000000_00000000_00000000, // pading 153 | 128'h00000000_00000000_00000000_00000000, // ... 154 | 128'h00000000_00000000_00000000_00000280 }; // ...padding, length 155 | 156 | reg [255:0] H_btc[0:2]; 157 | 158 | initial H_btc[0] = 159 | { 128'hdc6a3b8d_0c69421a_cb1a5434_e536f7d5, 160 | 128'hc3c1b9e4_4cbb9b8f_95f0172e_fc48d2df }; // after first block (job parameter) 161 | 162 | initial H_btc[1] = 163 | { 128'h0fc3bf25_9405a32f_d6d78a5e_6de88914, 164 | 128'h8edd4088_cc46a2eb_c604c45a_15fe7d15 }; // intermediate hash 165 | 166 | initial H_btc[2] = 167 | { 128'h766f7950_56dd74d7_03b9173e_8d44223b, 168 | 128'hdbe3e0b2_9fe6a0eb_8ab33534_88c2565c }; // expected result 169 | 170 | reg [255:0] H0 = 0; 171 | reg [255:0] H = 0; 172 | reg [511:0] M = 0; 173 | 174 | reg clk = 0; 175 | reg reset = 0; 176 | reg feedback = 0; 177 | 178 | reg [5:0] round = 0; 179 | reg [31:0] Kt = 0; 180 | 181 | wire [255:0] H_u0; 182 | wire [255:0] H_u1; 183 | 184 | sha_unit u0 ( 185 | clk, 186 | round, 187 | Kt, 188 | M, 189 | H0, 190 | H_u0 191 | ); 192 | 193 | sha_unit u1 ( 194 | clk, 195 | round, 196 | Kt, 197 | { H_u0[255:224], H[223:0], 1'b1, 191'd0, 64'h00000000_00000100 }, 198 | SHA256_H0, 199 | H_u1 200 | ); 201 | 202 | reg [15:0] i; 203 | 204 | initial 205 | begin 206 | #1 clk = 0; 207 | 208 | // Test case #1 209 | 210 | // Initialize inputs 211 | #1 M = M_fips1; 212 | #1 H0 = SHA256_H0; 213 | 214 | $display("Test #1 - Block #1:"); 215 | $display(" M:"); 216 | $display(" %h", M[511:384]); 217 | $display(" %h", M[383:256]); 218 | $display(" %h", M[255:128]); 219 | $display(" %h", M[127: 0]); 220 | $display(" H (in):"); 221 | $display(" %h", H0[255:128]); 222 | $display(" %h", H0[127: 0]); 223 | 224 | #1 round = 0; 225 | #1 Kt = K[0]; 226 | 227 | // Clock 64 times 228 | for (i = 0; i < 64; i = i + 1) 229 | begin 230 | 231 | #1 clk = 1; 232 | #1 clk = 0; 233 | 234 | #1 Kt = K[round]; 235 | #1 round = round + 1; 236 | 237 | `ifdef DEBUG_VERBOSE 238 | $display("[%2d] Kt %h, Wt %h", round, Kt, u0.Wt); 239 | $display("[%2d] S0: %h", round, u0.S0); 240 | $display("[%2d] S1: %h", round, u0.S1); 241 | $display("[%2d] H0: %h", round, u0.H0); 242 | $display("[%2d] H1: %h", round, u0.H1); 243 | /* 244 | $display("[%2d] W: %h %h %h %h", round, 32{x}, u.W[`idx32(14)], u.W[`idx32(13)], u.W[`idx32(12)]); 245 | $display("[%2d] %h %h %h %h", round, u.W[`idx32(11)], u.W[`idx32(10)], u.W[`idx32( 9)], u.W[`idx32( 8)]); 246 | $display("[%2d] %h %h %h %h", round, u.W[`idx32( 7)], u.W[`idx32( 6)], u.W[`idx32( 5)], u.W[`idx32( 4)]); 247 | $display("[%2d] %h %h %h %h", round, u.W[`idx32( 3)], u.W[`idx32( 2)], u.W[`idx32( 1)], u.W[`idx32( 0)]); 248 | */ 249 | `endif 250 | end 251 | 252 | `ifdef DEBUG_VERBOSE 253 | $display("[%2d] H1: %h", round, u0.H1); 254 | `endif 255 | 256 | $display(" H (out):"); 257 | $display(" %h", H_u0[255:128]); 258 | $display(" %h", H_u0[127: 0]); 259 | $display(" H (expected):"); 260 | $display(" %h", H_fips1[255:128]); 261 | $display(" %h", H_fips1[127: 0]); 262 | $display(""); 263 | 264 | /* 265 | // TEST #2 266 | 267 | // Initialize inputs 268 | M = M_fips2[0]; 269 | Hin = SHA256_H0; 270 | 271 | $display("Test #2 - Block #1:"); 272 | $display(" M:"); 273 | $display(" %h", M[511:384]); 274 | $display(" %h", M[383:256]); 275 | $display(" %h", M[255:128]); 276 | $display(" %h", M[127: 0]); 277 | $display(" H (in):"); 278 | $display(" %h", Hin[255:128]); 279 | $display(" %h", Hin[127: 0]); 280 | 281 | // ~> IDLE 282 | #1 reset = 1; 283 | #1 round = 0; 284 | #1 feedback = 0; // Disable feedback for this case 285 | 286 | #1 clk = 1; 287 | #1 clk = 0; 288 | #1 reset = 0; 289 | 290 | // Chew first block 291 | for (i = 0; i < 64; i = i + 1) 292 | begin 293 | // 0) IDLE ~> WORKING 294 | // 1-62) WORKING ~> WORKING 295 | // 63) WORKING ~> DONE 296 | #1 clk = 1; 297 | #1 clk = 0; 298 | 299 | #1 Kt = K[round]; 300 | #1 round = round + 1; 301 | end 302 | 303 | // DONE ~> IDLE (required when feedback = 0) 304 | #1 reset = 1; 305 | #1 round = 0; 306 | 307 | #1 clk = 1; 308 | #1 clk = 0; 309 | #1 reset = 0; 310 | 311 | $display(" H (out):"); 312 | $display(" %h", Hout[255:128]); 313 | $display(" %h", Hout[127: 0]); 314 | $display(" H (expected):"); 315 | $display(" %h", H_fips2[0][255:128]); 316 | $display(" %h", H_fips2[0][127: 0]); 317 | $display(""); 318 | 319 | // Prepare for second block 320 | M = M_fips2[1]; 321 | Hin = Hout; 322 | 323 | // Chew second block 324 | $display("Test #2 - Block #2:"); 325 | $display(" M:"); 326 | $display(" %h", M[511:384]); 327 | $display(" %h", M[383:256]); 328 | $display(" %h", M[255:128]); 329 | $display(" %h", M[127: 0]); 330 | $display(" H (in):"); 331 | $display(" %h", Hin[255:128]); 332 | $display(" %h", Hin[127: 0]); 333 | 334 | for (i = 0; i < 64; i = i + 1) 335 | begin 336 | // 0) IDLE ~> WORKING 337 | // 1-62) WORKING ~> WORKING 338 | // 63) WORKING ~> DONE 339 | #1 clk = 1; 340 | #1 clk = 0; 341 | #1 Kt = K[round]; 342 | round = round + 1; 343 | end 344 | 345 | // Extra clock cycle for updating result 346 | #1 clk = 1; 347 | #1 clk = 0; 348 | 349 | $display(" H (out):"); 350 | $display(" %h", Hout[255:128]); 351 | $display(" %h", Hout[127: 0]); 352 | $display(" H (expected):"); 353 | $display(" %h", H_fips2[1][255:128]); 354 | $display(" %h", H_fips2[1][127: 0]); 355 | $display(""); 356 | */ 357 | 358 | // TEST #3 359 | 360 | /* 361 | // Initialize inputs 362 | M = M_btc[0]; 363 | Hin = SHA256_H0; 364 | 365 | // This block will be done on host, intermediate state provided as 366 | // parameter. 367 | 368 | $display("Test #3 - Block #1:"); 369 | $display(" M:"); 370 | $display(" %h", M[511:384]); 371 | $display(" %h", M[383:256]); 372 | $display(" %h", M[255:128]); 373 | $display(" %h", M[127: 0]); 374 | $display(" H (in):"); 375 | $display(" %h", Hin[255:128]); 376 | $display(" %h", Hin[127: 0]); 377 | 378 | 379 | // ~> IDLE 380 | #1 reset = 1; 381 | #1 round = 0; 382 | #1 feedback = 0; 383 | 384 | #1 clk = 1; 385 | #1 clk = 0; 386 | #1 reset = 0; 387 | 388 | // Chew first block 389 | for (i = 0; i < 64; i = i + 1) 390 | begin 391 | #1 clk = 1; 392 | #1 clk = 0; 393 | #1 Kt = K[round]; 394 | round = round + 1; 395 | end 396 | 397 | // DONE ~> IDLE (required when feedback = 0) 398 | #1 reset = 1; 399 | #1 round = 0; 400 | #1 feedback = 1; // exploit feedback for this case 401 | 402 | #1 clk = 1; 403 | #1 clk = 0; 404 | #1 reset = 0; 405 | 406 | $display(" H (out):"); 407 | $display(" %h", Hout[255:128]); 408 | $display(" %h", Hout[127: 0]); 409 | $display(" H (expected):"); 410 | $display(" %h", H_btc[0][255:128]); 411 | $display(" %h", H_btc[0][127: 0]); 412 | $display(""); 413 | */ 414 | 415 | // Prepare for second block -- where we normally start 416 | M = M_btc[1]; 417 | H0 = H_btc[0]; 418 | 419 | $display("Test #3 - Block #2:"); 420 | $display(" M:"); 421 | $display(" %h", M[511:384]); 422 | $display(" %h", M[383:256]); 423 | $display(" %h", M[255:128]); 424 | $display(" %h", M[127: 0]); 425 | $display(" H (in):"); 426 | $display(" %h", H0[255:128]); 427 | $display(" %h", H0[127: 0]); 428 | 429 | // Chew second block 430 | for (i = 0; i < 64; i = i + 1) 431 | begin 432 | #1 clk = 1; 433 | #1 clk = 0; 434 | #1 Kt = K[round]; 435 | #1 round = round + 1; 436 | end 437 | 438 | // Second verse same as the first 439 | $display(" H_u0 (actual):"); 440 | $display(" %h", H_u0[255:128]); 441 | $display(" %h", H_u0[127: 0]); 442 | $display(" H_u0 (expected):"); 443 | $display(" %h", H_btc[1][255:128]); 444 | $display(" %h", H_btc[1][127: 0]); 445 | $display(""); 446 | 447 | 448 | $display(" M1:"); 449 | $display(" %h", u1.M[511:384]); 450 | $display(" %h", u1.M[383:256]); 451 | $display(" %h", u1.M[255:128]); 452 | $display(" %h", u1.M[127: 0]); 453 | 454 | // Save H_u0 for use in M for second block 455 | // -- only [223:0] needs to be stored, [255:32] used as wire for use on 456 | // same cycle as round == 0. 457 | H[223:0] = H_u0[223:0]; 458 | 459 | for (i = 0; i < 64; i = i + 1) 460 | begin 461 | #1 clk = 1; 462 | #1 clk = 0; 463 | #1 Kt = K[round]; 464 | #1 round = round + 1; 465 | end 466 | 467 | $display("Test #3 - Block #3:"); 468 | $display(" H_u1 (actual):"); 469 | $display(" %h", H_u1[255:128]); 470 | $display(" %h", H_u1[127: 0]); 471 | $display(" H_u1 (expected):"); 472 | $display(" %h", H_btc[2][255:128]); 473 | $display(" %h", H_btc[2][127: 0]); 474 | $display(""); 475 | 476 | end 477 | 478 | 479 | endmodule 480 | -------------------------------------------------------------------------------- /src/tests/sha_unit_usage_tb.v: -------------------------------------------------------------------------------- 1 | `timescale 10ns/100ps 2 | 3 | module sha_unit_usage_tb(); 4 | 5 | /* 6 | Tests basic usage of `sha_unit`. 7 | 8 | Uses "FIPS single-block" example: "abc". 9 | */ 10 | 11 | reg [511:0] M_fips1 = 12 | { 128'h61626380_00000000_00000000_00000000, 13 | 128'h00000000_00000000_00000000_00000000, 14 | 128'h00000000_00000000_00000000_00000000, 15 | 128'h00000000_00000000_00000000_00000018 }; 16 | 17 | reg[255:0] H_fips1 = 18 | { 128'hba7816bf_8f01cfea_414140de_5dae2223, 19 | 128'hb00361a3_96177a9c_b410ff61_f20015ad }; 20 | 21 | `define VERILATOR 22 | //`define DEBUG_VERBOSE 23 | 24 | // SHA256 constants 25 | // TODO was reg[0:255] 26 | reg [255:0] SHA256_H0 = { 32'h6a09e667, 32'hbb67ae85, 27 | 32'h3c6ef372, 32'ha54ff53a, 28 | 32'h510e527f, 32'h9b05688c, 29 | 32'h1f83d9ab, 32'h5be0cd19 }; 30 | 31 | // TODO use SHA256_K.v 32 | localparam[2047:0] SHA256_K = 33 | { 32'h428a2f98, 32'h71374491, 32'hb5c0fbcf, 32'he9b5dba5, 34 | 32'h3956c25b, 32'h59f111f1, 32'h923f82a4, 32'hab1c5ed5, 35 | 32'hd807aa98, 32'h12835b01, 32'h243185be, 32'h550c7dc3, 36 | 32'h72be5d74, 32'h80deb1fe, 32'h9bdc06a7, 32'hc19bf174, 37 | 32'he49b69c1, 32'hefbe4786, 32'h0fc19dc6, 32'h240ca1cc, 38 | 32'h2de92c6f, 32'h4a7484aa, 32'h5cb0a9dc, 32'h76f988da, 39 | 32'h983e5152, 32'ha831c66d, 32'hb00327c8, 32'hbf597fc7, 40 | 32'hc6e00bf3, 32'hd5a79147, 32'h06ca6351, 32'h14292967, 41 | 32'h27b70a85, 32'h2e1b2138, 32'h4d2c6dfc, 32'h53380d13, 42 | 32'h650a7354, 32'h766a0abb, 32'h81c2c92e, 32'h92722c85, 43 | 32'ha2bfe8a1, 32'ha81a664b, 32'hc24b8b70, 32'hc76c51a3, 44 | 32'hd192e819, 32'hd6990624, 32'hf40e3585, 32'h106aa070, 45 | 32'h19a4c116, 32'h1e376c08, 32'h2748774c, 32'h34b0bcb5, 46 | 32'h391c0cb3, 32'h4ed8aa4a, 32'h5b9cca4f, 32'h682e6ff3, 47 | 32'h748f82ee, 32'h78a5636f, 32'h84c87814, 32'h8cc70208, 48 | 32'h90befffa, 32'ha4506ceb, 32'hbef9a3f7, 32'hc67178f2 }; 49 | 50 | wire [31:0] K[0:63]; 51 | 52 | assign K[ 0] = SHA256_K[2047:2016]; 53 | assign K[ 1] = SHA256_K[2015:1984]; 54 | assign K[ 2] = SHA256_K[1983:1952]; 55 | assign K[ 3] = SHA256_K[1951:1920]; 56 | assign K[ 4] = SHA256_K[1919:1888]; 57 | assign K[ 5] = SHA256_K[1887:1856]; 58 | assign K[ 6] = SHA256_K[1855:1824]; 59 | assign K[ 7] = SHA256_K[1823:1792]; 60 | assign K[ 8] = SHA256_K[1791:1760]; 61 | assign K[ 9] = SHA256_K[1759:1728]; 62 | assign K[10] = SHA256_K[1727:1696]; 63 | assign K[11] = SHA256_K[1695:1664]; 64 | assign K[12] = SHA256_K[1663:1632]; 65 | assign K[13] = SHA256_K[1631:1600]; 66 | assign K[14] = SHA256_K[1599:1568]; 67 | assign K[15] = SHA256_K[1567:1536]; 68 | assign K[16] = SHA256_K[1535:1504]; 69 | assign K[17] = SHA256_K[1503:1472]; 70 | assign K[18] = SHA256_K[1471:1440]; 71 | assign K[19] = SHA256_K[1439:1408]; 72 | assign K[20] = SHA256_K[1407:1376]; 73 | assign K[21] = SHA256_K[1375:1344]; 74 | assign K[22] = SHA256_K[1343:1312]; 75 | assign K[23] = SHA256_K[1311:1280]; 76 | assign K[24] = SHA256_K[1279:1248]; 77 | assign K[25] = SHA256_K[1247:1216]; 78 | assign K[26] = SHA256_K[1215:1184]; 79 | assign K[27] = SHA256_K[1183:1152]; 80 | assign K[28] = SHA256_K[1151:1120]; 81 | assign K[29] = SHA256_K[1119:1088]; 82 | assign K[30] = SHA256_K[1087:1056]; 83 | assign K[31] = SHA256_K[1055:1024]; 84 | assign K[32] = SHA256_K[1023: 992]; 85 | assign K[33] = SHA256_K[ 991: 960]; 86 | assign K[34] = SHA256_K[ 959: 928]; 87 | assign K[35] = SHA256_K[ 927: 896]; 88 | assign K[36] = SHA256_K[ 895: 864]; 89 | assign K[37] = SHA256_K[ 863: 832]; 90 | assign K[38] = SHA256_K[ 831: 800]; 91 | assign K[39] = SHA256_K[ 799: 768]; 92 | assign K[40] = SHA256_K[ 767: 736]; 93 | assign K[41] = SHA256_K[ 735: 704]; 94 | assign K[42] = SHA256_K[ 703: 672]; 95 | assign K[43] = SHA256_K[ 671: 640]; 96 | assign K[44] = SHA256_K[ 639: 608]; 97 | assign K[45] = SHA256_K[ 607: 576]; 98 | assign K[46] = SHA256_K[ 575: 544]; 99 | assign K[47] = SHA256_K[ 543: 512]; 100 | assign K[48] = SHA256_K[ 511: 480]; 101 | assign K[49] = SHA256_K[ 479: 448]; 102 | assign K[50] = SHA256_K[ 447: 416]; 103 | assign K[51] = SHA256_K[ 415: 384]; 104 | assign K[52] = SHA256_K[ 383: 352]; 105 | assign K[53] = SHA256_K[ 351: 320]; 106 | assign K[54] = SHA256_K[ 319: 288]; 107 | assign K[55] = SHA256_K[ 287: 256]; 108 | assign K[56] = SHA256_K[ 255: 224]; 109 | assign K[57] = SHA256_K[ 223: 192]; 110 | assign K[58] = SHA256_K[ 191: 160]; 111 | assign K[59] = SHA256_K[ 159: 128]; 112 | assign K[60] = SHA256_K[ 127: 96]; 113 | assign K[61] = SHA256_K[ 95: 64]; 114 | assign K[62] = SHA256_K[ 63: 32]; 115 | assign K[63] = SHA256_K[ 31: 0]; 116 | 117 | reg clk = 0; 118 | reg [5:0] round = 0; 119 | reg [31:0] Kt = SHA256_K[2047:2016]; 120 | 121 | wire [255:0] H; 122 | 123 | sha_unit uut ( 124 | // Control 125 | .clk(clk), 126 | // Externally managed/shared state 127 | .round(round), 128 | .Kt(Kt), 129 | // SHA256 parameters 130 | .M(M_fips1), 131 | .H0(SHA256_H0), 132 | // Result 133 | .H1(H) 134 | ); 135 | 136 | always 137 | begin 138 | #1 clk <= !clk; 139 | end 140 | 141 | always @(posedge clk) 142 | begin 143 | if (round == 63) 144 | round <= 0; 145 | else 146 | round <= round + 1; 147 | end 148 | 149 | always @(posedge clk) 150 | begin 151 | Kt <= K[round]; 152 | end 153 | 154 | reg [15:0] i; 155 | 156 | initial 157 | begin 158 | 159 | $dumpfile("sha_unit_usage_tb.vcd"); 160 | $dumpvars; 161 | 162 | // Initialize inputs 163 | round <= 0; 164 | Kt <= K[0]; 165 | clk <= 0; 166 | 167 | // Clock 64 times 168 | for (i = 0; i < 64; i = i + 1) 169 | begin 170 | 171 | #1; // rising edge 172 | 173 | `ifdef DEBUG_VERBOSE 174 | $display("[%2d] Kt %h, Wt %h", round, Kt, uut.Wt); 175 | $display("[%2d] S0: %h %h %h %h %h %h %h %h", round, uut.S0[255:224], uut.S0[223:192], uut.S0[191:160], uut.S0[159:128], uut.S0[127:96], uut.S0[95:64], uut.S0[63:32], uut.S0[31:0]); 176 | $display("[%2d] S1: %h %h %h %h %h %h %h %h", round, uut.S1[255:224], uut.S1[223:192], uut.S1[191:160], uut.S1[159:128], uut.S1[127:96], uut.S1[95:64], uut.S1[63:32], uut.S1[31:0]); 177 | $display("[%2d] H0: %h %h %h %h %h %h %h %h", round, uut.H0[255:224], uut.H0[223:192], uut.H0[191:160], uut.H0[159:128], uut.H0[127:96], uut.H0[95:64], uut.H0[63:32], uut.H0[31:0]); 178 | $display("[%2d] H1: %h %h %h %h %h %h %h %h", round, uut.H1[255:224], uut.H1[223:192], uut.H1[191:160], uut.H1[159:128], uut.H1[127:96], uut.H1[95:64], uut.H1[63:32], uut.H1[31:0]); 179 | $display(""); 180 | `endif 181 | 182 | #1; // falling edge 183 | 184 | end 185 | if (H == H_fips1) 186 | begin 187 | $display("\033\133\063\062\155[PASS]\033\133\060\155 `sha_unit`: FIPS-1 Single block"); 188 | end 189 | else 190 | begin 191 | $display("\n\033\133\063\061\155[FAIL]\033\133\060\155 `sha_unit`: FIPS-1 Single block"); 192 | $display(" H0 = %h", SHA256_H0[255:128]); 193 | $display(" %h", SHA256_H0[127: 0]); 194 | $display(" M = %h", M_fips1[511:384]); 195 | $display(" %h", M_fips1[383:256]); 196 | $display(" %h", M_fips1[255:128]); 197 | $display(" %h", M_fips1[127: 0]); 198 | $display(" H (actual) = %h", H[255:128]); 199 | $display(" %h", H[127: 0]); 200 | $display(" H (expected) = %h", H_fips1[255:128]); 201 | $display(" %h", H_fips1[127: 0]); 202 | 203 | $error("Test case failed."); 204 | end 205 | 206 | $finish; 207 | end 208 | 209 | 210 | endmodule 211 | -------------------------------------------------------------------------------- /src/tests/shapool_usage_tb.v: -------------------------------------------------------------------------------- 1 | `timescale 10ns/100ps 2 | 3 | module shapool_test(); 4 | 5 | /* 6 | Tests basic usage of `shapool`. 7 | 8 | Uses only one sha_unit (POOL_SIZE = 1). 9 | */ 10 | 11 | `define VERILATOR 12 | //`define DEBUG_VERBOSE 13 | 14 | `define SHAPOOL_NO_NONCE_OFFSET // Required for POOL_SIZE = 1 15 | 16 | localparam POOL_SIZE = 1; 17 | localparam POOL_SIZE_LOG2 = 0; 18 | localparam BASE_TARGET = 4; 19 | 20 | localparam [359:0] job_parameters = { 21 | 128'hdc6a3b8d_0c69421a_cb1a5434_e536f7d5, // SHA starting state 22 | 128'hc3c1b9e4_4cbb9b8f_95f0172e_fc48d2df, // ... 23 | 96'hdc141787_358b0553_535f0119 // Start of message block 24 | }; 25 | 26 | localparam [7:0] device_parameters = { 27 | 8'h00 // nonce start (MSB) 28 | }; 29 | 30 | reg clk = 0; 31 | reg reset_n = 1; 32 | 33 | reg [255:0] sha_state; 34 | reg [95:0] message_head; 35 | reg [7:0] nonce_start_MSB; 36 | 37 | wire success; 38 | wire [31:0] nonce; 39 | wire [7:0] match_flags; 40 | 41 | localparam nonce_expected = 39; 42 | localparam [255:0] H_expected = { 256'hc7f3244e501edf780c420f63a4266d30ffe1bdb53f4fde3ccd688604f15ffd03 }; 43 | 44 | reg [255:0] H_btc [0:2]; 45 | 46 | // State (a,b,c,...) after first block of first hash 47 | initial H_btc[0] = 48 | { 128'hdc6a3b8d_0c69421a_cb1a5434_e536f7d5, 49 | 128'hc3c1b9e4_4cbb9b8f_95f0172e_fc48d2df }; 50 | 51 | // Expected result of first hash 52 | initial H_btc[1] = 53 | { 128'h0fc3bf25_9405a32f_d6d78a5e_6de88914, 54 | 128'h8edd4088_cc46a2eb_c604c45a_15fe7d15 }; 55 | 56 | // Expected result of second hash 57 | initial H_btc[2] = 58 | { 128'h766f7950_56dd74d7_03b9173e_8d44223b, 59 | 128'hdbe3e0b2_9fe6a0eb_8ab33534_88c2565c }; 60 | 61 | /* 62 | 0 5c56c2883435b38aeba0e69fb2e0e3db3b22448d3e17b903d774dd5650796f76 63 | 1 28902a23a194dee94141d1b70102accd85fc2c1ead0901ba0e41ade90d38a08e 64 | 2 729577af82250aaf9e44f70a72814cf56c16d430a878bf52fdaceeb7b4bd37f4 65 | 3 8491452381016cf80562ff489e492e00331de3553178c73c5169574000f1ed1c 66 | 39 03fd5ff1048668cd3cde4f3fb5bde1ff306d26a4630f420c78df1e504e24f3c7 67 | c7f3244e501edf780c420f63a4266d30ffe1bdb53f4fde3ccd688604f15ffd03 (output of module) 68 | 990 0001e3a4583f4c6d81251e8d9901dbe0df74d7144300d7c03cab15eca04bd4bb 69 | */ 70 | 71 | shapool 72 | #(.POOL_SIZE(POOL_SIZE), 73 | .POOL_SIZE_LOG2(POOL_SIZE_LOG2), 74 | .BASE_TARGET(BASE_TARGET)) 75 | uut ( 76 | // Control 77 | clk, 78 | reset_n, 79 | // Job Params 80 | sha_state, 81 | message_head, 82 | nonce_start_MSB, 83 | // Result 84 | success, 85 | nonce, 86 | match_flags 87 | ); 88 | 89 | localparam spi_bit_half_period = 6; 90 | localparam reset_hold_period = 2; 91 | 92 | always 93 | begin 94 | #1 clk = !clk; 95 | end 96 | 97 | reg [31:0] i = 0; 98 | reg [31:0] n = 0; 99 | 100 | initial 101 | begin 102 | 103 | $dumpfile("shapool_usage_tb.vcd"); 104 | $dumpvars; 105 | 106 | // Initial values 107 | clk <= 0; 108 | reset_n <= 1; 109 | 110 | // Job parameters 111 | sha_state <= job_parameters[351:96]; 112 | message_head <= job_parameters[ 95: 0]; 113 | 114 | // Device configuration 115 | nonce_start_MSB <= device_parameters[7:0]; 116 | 117 | #10; 118 | 119 | // Reset module 120 | reset_n <= 0; 121 | #reset_hold_period; 122 | 123 | // (Normally job_parameters, device_parameters shifted in here.) 124 | 125 | reset_n <= 1; 126 | #2; // one clock cycle (INIT -> EXEC) 127 | 128 | for (n = 0; n < 50 && !success; n = n) 129 | begin 130 | 131 | for (i = 0; i < 64 && !success; i = i + 1) 132 | begin 133 | #1; // rising edge 134 | #1; // falling edge 135 | 136 | `ifdef DEBUG_VERBOSE 137 | $display(" success: %b", uut.success); 138 | $display(" round: %d", uut.round); 139 | $display(" nonce: %d", nonce); 140 | $display(" u0:"); 141 | $display(" M: %h", uut.pipelines[0].u0.M[511:384]); 142 | $display(" %h", uut.pipelines[0].u0.M[383:256]); 143 | $display(" %h", uut.pipelines[0].u0.M[255:128]); 144 | $display(" %h", uut.pipelines[0].u0.M[127: 0]); 145 | $display(" H: %h", uut.pipelines[0].H_u0[255:128]); 146 | $display(" %h", uut.pipelines[0].H_u0[127: 0]); 147 | $display(" u1:"); 148 | $display(" M: %h", uut.pipelines[0].u1.M[511:384]); 149 | $display(" %h", uut.pipelines[0].u1.M[383:256]); 150 | $display(" %h", uut.pipelines[0].u1.M[255:128]); 151 | $display(" %h", uut.pipelines[0].u1.M[127: 0]); 152 | $display(" H: %h", uut.pipelines[0].H_u1[255:128]); 153 | $display(" %h", uut.pipelines[0].H_u1[127: 0]); 154 | $display(""); 155 | `endif 156 | end 157 | 158 | end 159 | 160 | if (success == 1 && nonce - 2 == nonce_expected && uut.pipelines[0].H_u1 == H_expected) 161 | begin 162 | $display("\033\133\063\062\155[PASS]\033\133\060\155 `shapool`: single track, BTC four-zeroes"); 163 | end 164 | else 165 | begin 166 | $display("\033\133\063\061\155[FAIL]\033\133\060\155 `shapool`: single track, BTC four-zeroes"); 167 | $display(""); 168 | $display(" success = %0d", success); 169 | $display(" nonce = %0d", nonce); 170 | $display(" nonce (adjusted) = %0d", nonce-2); 171 | $display(" H (actual) = %h", uut.pipelines[0].H_u1[255:128]); 172 | $display(" %h", uut.pipelines[0].H_u1[127: 0]); 173 | $display(" H (expected) = %h", H_expected[255:128]); 174 | $display(" %h", H_expected[127: 0]); 175 | $error("Test case failed."); 176 | end 177 | 178 | $finish; 179 | end 180 | 181 | endmodule -------------------------------------------------------------------------------- /src/tests/top_usage_tb.v: -------------------------------------------------------------------------------- 1 | `timescale 1us/10ns 2 | 3 | module test_top(); 4 | 5 | `define VERILATOR 6 | 7 | reg clk_in = 0; 8 | 9 | reg reset_n_in = 0; 10 | 11 | reg sck0_in; 12 | reg sdi0_in; 13 | reg cs0_n_in; 14 | 15 | reg sck1_in; 16 | reg sdi1_in; 17 | wire sdo1_out; 18 | reg cs1_n_in; 19 | 20 | wire ready_n_ts_inout; 21 | 22 | wire status_led_n_out; 23 | 24 | `define SHAPOOL_NO_NONCE_OFFSET // Required for POOL_SIZE = 1: 25 | 26 | localparam POOL_SIZE = 1; 27 | localparam POOL_SIZE_LOG2 = 0; 28 | localparam BASE_TARGET = 3; 29 | 30 | top 31 | #(.POOL_SIZE(POOL_SIZE), 32 | .POOL_SIZE_LOG2(POOL_SIZE_LOG2), 33 | .BASE_TARGET(BASE_TARGET)) 34 | uut ( 35 | clk_in, 36 | reset_n_in, 37 | // Global data 38 | sck0_in, 39 | sdi0_in, 40 | cs0_n_in, 41 | // Daisy data 42 | sck1_in, 43 | sdi1_in, 44 | sdo1_out, 45 | cs1_n_in, 46 | // Success flags 47 | ready_n_ts_inout, 48 | // Indicators 49 | status_led_n_out 50 | ); 51 | 52 | // Test case 53 | 54 | reg [31:0] n = 0; 55 | reg [31:0] i = 0; 56 | 57 | localparam [359:0] test_spi0_data0 = { 58 | 128'hdc6a3b8d_0c69421a_cb1a5434_e536f7d5, // SHA starting state 59 | 128'hc3c1b9e4_4cbb9b8f_95f0172e_fc48d2df, // ... 60 | 96'hdc141787_358b0553_535f0119 // Start of message block 61 | }; 62 | 63 | localparam [7:0] test_spi1_data0 = { 64 | 8'h00 // nonce start (MSB) 65 | }; 66 | 67 | reg [359:0] test_spi0_data; 68 | reg [7:0] test_spi1_data; 69 | reg [31:0] result; 70 | 71 | initial 72 | begin 73 | 74 | $dumpfile("test_top.vcd"); 75 | $dumpvars; 76 | 77 | test_spi0_data = test_spi0_data0; 78 | test_spi1_data = test_spi1_data0; 79 | 80 | // Initial states 81 | clk_in = 0; 82 | reset_n_in = 1; 83 | sck0_in = 0; 84 | cs0_n_in = 1; 85 | sck1_in = 0; 86 | cs1_n_in = 1; 87 | 88 | // Deassert reset_n_in to enter STATE_IDLE 89 | // -- to read in data on SPI 90 | #1 reset_n_in = 0; 91 | 92 | /////////////////////////////////// 93 | // Clock-in device configuration // 94 | /////////////////////////////////// 95 | 96 | // Assert CS1 97 | #1 cs1_n_in = 0; 98 | 99 | for (i = 0; i < 8; i = i + 1) 100 | begin 101 | // Shift-in data msb-first 102 | #1 sdi1_in = test_spi1_data[7]; 103 | test_spi1_data <= { test_spi1_data[6:0], 1'b0 }; 104 | 105 | #1 sck1_in = 1; 106 | 107 | // Need 3 clock cycles to synchronize sck1_in 108 | #1 clk_in = 1; 109 | #1 clk_in = 0; 110 | #1 clk_in = 1; 111 | #1 clk_in = 0; 112 | #1 clk_in = 1; 113 | #1 clk_in = 0; 114 | 115 | #1 sck1_in = 0; 116 | 117 | // Need 3 clock cycles to synchronize sck1_in 118 | #1 clk_in = 1; 119 | #1 clk_in = 0; 120 | #1 clk_in = 1; 121 | #1 clk_in = 0; 122 | #1 clk_in = 1; 123 | #1 clk_in = 0; 124 | end 125 | 126 | #1 cs1_n_in = 1; 127 | 128 | ///////////////////////////// 129 | // Clock-in job parameters // 130 | ///////////////////////////// 131 | 132 | #1 cs0_n_in = 0; 133 | 134 | for (i = 0; i < 360; i = i + 1) 135 | begin 136 | // Simulate FIFO into device 137 | #1 sdi0_in = test_spi0_data[351]; 138 | test_spi0_data <= { test_spi0_data[350:0], 1'b0 }; 139 | 140 | #1 sck0_in = 1; 141 | 142 | // Need 3 clock cycles to synchronize sck1_in 143 | #1 clk_in = 1; 144 | #1 clk_in = 0; 145 | #1 clk_in = 1; 146 | #1 clk_in = 0; 147 | #1 clk_in = 1; 148 | #1 clk_in = 0; 149 | 150 | #1 sck0_in = 0; 151 | 152 | // Need 3 clock cycles to synchronize sck1_in 153 | #1 clk_in = 1; 154 | #1 clk_in = 0; 155 | #1 clk_in = 1; 156 | #1 clk_in = 0; 157 | #1 clk_in = 1; 158 | #1 clk_in = 0; 159 | end 160 | 161 | #1 cs0_n_in = 1; 162 | 163 | $display("Device configuration"); 164 | $display(" nonce start MSB: %h", uut.nonce_start); 165 | 166 | $display("Job parameters:"); 167 | $display(" SHA256 state: %h", uut.sha_state[255:128]); 168 | $display(" %h", uut.sha_state[127:0]); 169 | $display(" message head: %h", uut.message_head); 170 | 171 | ///////////////////////////////// 172 | // Reset and run until success // 173 | ///////////////////////////////// 174 | 175 | // Deassert reset_n_in to enter STATE_EXEC 176 | #1 reset_n_in = 1; 177 | 178 | for (n = 0; n < 50 && !ready_n_ts_inout; n = n + 1) 179 | begin 180 | 181 | for (i = 0; i < 64; i = i + 1) 182 | begin 183 | $display("nonce: %d", uut.pool.nonce); 184 | $display("round: %d", uut.pool.round); 185 | $display(" Kt: %h", uut.pool.Kt); 186 | $display(" success: %b", uut.pool.success); 187 | $display(" |match_flags: %b", |uut.pool.match_flags); 188 | $display(" round == 0: %b", uut.pool.round == 0); 189 | $display(" skip_first: %b", uut.pool.skip_first); 190 | $display(" skip_second: %b", uut.pool.skip_second); 191 | $display(" u0.M: %h", uut.pool.tracks[0].u0.M[511:384]); 192 | $display(" u0.M: %h", uut.pool.tracks[0].u0.M[383:256]); 193 | $display(" u0.M: %h", uut.pool.tracks[0].u0.M[255:128]); 194 | $display(" u0.M: %h", uut.pool.tracks[0].u0.M[127:0]); 195 | $display(" u0.Wt: %h", uut.pool.tracks[0].u0.Wt); 196 | $display(" H_u0: %h", uut.pool.tracks[0].H_u0); 197 | $display(" u1.M: %h", uut.pool.tracks[0].u1.M[511:384]); 198 | $display(" u1.M: %h", uut.pool.tracks[0].u1.M[383:256]); 199 | $display(" u1.M: %h", uut.pool.tracks[0].u1.M[255:128]); 200 | $display(" u1.M: %h", uut.pool.tracks[0].u1.M[127:0]); 201 | $display(" u1.Wt: %h", uut.pool.tracks[0].u1.Wt); 202 | $display(" H_u1 (bs): %h", uut.pool.tracks[0].H_bs); 203 | $display(" H: %h", uut.pool.tracks[0].H); 204 | $display(" test bits: %h", { uut.pool.tracks[0].H[BASE_TARGET+16-1:16], uut.pool.tracks[0].H[15:0] & uut.pool.difficulty_bm }); 205 | $display(""); 206 | 207 | #1 clk_in = 1; 208 | #1 clk_in = 0; 209 | end 210 | 211 | end 212 | 213 | // Extra clock to save output/result 214 | #1 clk_in = 1; 215 | #1 clk_in = 0; 216 | 217 | $display("nonce: %h", uut.nonce); 218 | $display("ext_io.result_data: %h", uut.ext_io.result_data); 219 | 220 | // Extra clock to save result to output buffer 221 | #1 clk_in = 1; 222 | #1 clk_in = 0; 223 | 224 | $display("nonce: %h", uut.nonce); 225 | $display("ext_io.result_data: %h", uut.ext_io.result_data); 226 | 227 | // Shift result out over SPI1 228 | #1 cs1_n_in = 0; 229 | 230 | for (i = 0; i < 32; i = i + 1) 231 | begin 232 | $display("[%d] (%b) %b", i, ready_n_ts_inout, sdo1_out); 233 | 234 | #1 sck1_in = 1; 235 | 236 | // Need 3 clock cycles to synchronize sck1_in 237 | #1 clk_in = 1; 238 | #1 clk_in = 0; 239 | #1 clk_in = 1; 240 | #1 clk_in = 0; 241 | #1 clk_in = 1; 242 | #1 clk_in = 0; 243 | 244 | result <= { result[30:0], sdo1_out }; 245 | 246 | #1 sck1_in = 0; 247 | 248 | // Need 3 clock cycles to synchronize sck1_in 249 | #1 clk_in = 1; 250 | #1 clk_in = 0; 251 | #1 clk_in = 1; 252 | #1 clk_in = 0; 253 | #1 clk_in = 1; 254 | #1 clk_in = 0; 255 | end 256 | 257 | #1 cs1_n_in = 1; 258 | 259 | $display("top_test result: %h", result); 260 | 261 | end 262 | 263 | endmodule 264 | -------------------------------------------------------------------------------- /src/top.v: -------------------------------------------------------------------------------- 1 | /* 2 | * TODO redo documentation 3 | */ 4 | module top 5 | ( 6 | clk_in, 7 | reset_n_in, 8 | // Global data 9 | sck0_in, 10 | sdi0_in, 11 | cs0_n_in, 12 | // Daisy data 13 | sck1_in, 14 | sdi1_in, 15 | sdo1_out, 16 | cs1_n_in, 17 | // READY flags 18 | ready_n_od_out, 19 | // Indicator LED 20 | status_led_n_out 21 | ); 22 | 23 | localparam NONCE_WIDTH = 32 - POOL_SIZE_LOG2; 24 | 25 | parameter POOL_SIZE = 2; 26 | parameter POOL_SIZE_LOG2 = 1; 27 | 28 | // Target range in number of leading zeros: 29 | // * minimum: 32 30 | // * maximum: 224 31 | // 32 | // TARGET example: 33 | // 34 | // Difficulty = 4022059196164 35 | // 36 | // FLOOR(LOG2(0x00000000ffffffff...ffff / 4022059196164)) 37 | // = 182 38 | // 39 | // Therefore, 2^182 is nearest power-2 target less than the actual 40 | // target and 74 (=256-182) leading zeros are required to be <= 2^182. 41 | 42 | parameter BASE_TARGET = 32; 43 | 44 | // Inputs and Outputs 45 | 46 | input wire clk_in; 47 | 48 | input wire reset_n_in; 49 | 50 | input wire sck0_in; 51 | input wire sdi0_in; 52 | input wire cs0_n_in; 53 | 54 | input wire sck1_in; 55 | input wire sdi1_in; 56 | output wire sdo1_out; 57 | input wire cs1_n_in; 58 | 59 | output wire ready_n_od_out; 60 | 61 | output wire status_led_n_out; 62 | 63 | // Global reset(s) 64 | wire g_reset_n; 65 | 66 | wire core_reset_n; 67 | wire g_core_reset_n; 68 | 69 | `ifdef VERILATOR 70 | assign g_reset_n = reset_n_in; 71 | assign g_core_reset_n = core_reset_n; 72 | `else 73 | // Buffered external `reset_n` 74 | SB_GB reset_gbuf ( 75 | .USER_SIGNAL_TO_GLOBAL_BUFFER(reset_n_in), 76 | .GLOBAL_BUFFER_OUTPUT(g_reset_n) 77 | ); 78 | 79 | // Hold the core in a reset state when either the 80 | // external `reset_n` is low or `ready` is high. 81 | SB_GB core_reset_gbuf ( 82 | .USER_SIGNAL_TO_GLOBAL_BUFFER(core_reset_n), 83 | .GLOBAL_BUFFER_OUTPUT(g_core_reset_n) 84 | ); 85 | `endif 86 | 87 | // Device parameters 88 | // * 8' nonce starting count 89 | wire [7:0] device_config; 90 | wire [7:0] nonce_start; 91 | 92 | assign nonce_start = device_config[7:0]; 93 | 94 | // Job parameters 95 | wire [351:0] job_config; 96 | // * 256' initial SHA256 state 97 | // * 96' start of first message block 98 | wire [255:0] sha_state; 99 | wire [95:0] message_head; 100 | 101 | assign sha_state = job_config[351:96]; 102 | assign message_head = job_config[ 95: 0]; 103 | 104 | // `shapool` results 105 | wire success; 106 | wire [31:0] nonce; 107 | wire [7:0] match_flags; 108 | 109 | // External READY flag (drives open-drain `ready_n_od_out`) 110 | wire ready; 111 | 112 | assign ready_n_od_out = ready ? 1'b0 : 1'bz; 113 | 114 | // External IO interface 115 | external_io #( 116 | .POOL_SIZE(POOL_SIZE), 117 | .POOL_SIZE_LOG2(POOL_SIZE_LOG2) 118 | ) ext_io ( 119 | clk_in, 120 | g_reset_n, 121 | // SPI(0) 122 | sck0_in, 123 | sdi0_in, 124 | cs0_n_in, 125 | // SPI(1) 126 | sck1_in, 127 | sdi1_in, 128 | sdo1_out, 129 | cs1_n_in, 130 | // Stored data 131 | device_config, 132 | job_config, 133 | // Control signals 134 | core_reset_n, 135 | // From shapool 136 | success, 137 | { match_flags, nonce }, 138 | // READY signal 139 | ready 140 | ); 141 | 142 | // Hasher pool 143 | shapool #( 144 | .POOL_SIZE(POOL_SIZE), 145 | .POOL_SIZE_LOG2(POOL_SIZE_LOG2), 146 | .BASE_TARGET(BASE_TARGET) 147 | ) 148 | pool ( 149 | // Control 150 | .clk(clk_in), 151 | .reset_n(g_core_reset_n), 152 | // Parameters 153 | .sha_state(sha_state), 154 | .message_head(message_head), 155 | .nonce_start_MSB(nonce_start), 156 | // Results 157 | .success(success), 158 | .nonce(nonce), 159 | .match_flags(match_flags) 160 | ); 161 | 162 | assign status_led_n_out = g_core_reset_n; 163 | 164 | endmodule 165 | -------------------------------------------------------------------------------- /src/top_hx8k.v: -------------------------------------------------------------------------------- 1 | `include "target.vh" 2 | 3 | module top_hx8k 4 | ( 5 | clk_in, 6 | reset_n_in, 7 | // Global data 8 | sck0_in, 9 | sdi0_in, 10 | cs0_n_in, 11 | // Daisy data 12 | sck1_in, 13 | sdi1_in, 14 | sdo1_out, 15 | cs1_n_in, 16 | // READY flags 17 | ready_n_od_out, 18 | // Indicator LED 19 | status_led_n_out 20 | ); 21 | localparam POOL_SIZE = 2; 22 | localparam POOL_SIZE_LOG2 = 1; 23 | localparam BASE_TARGET = `TARGET; 24 | 25 | // 12 MHz ~ 30 MHz 26 | localparam PLL_DIVR = 4'b0000; 27 | localparam PLL_DIVF = 7'b1001111; 28 | localparam PLL_DIVQ = 3'b101; 29 | 30 | // Multiply input clock signal using SB_PLL40_CORE 31 | wire g_clk; 32 | 33 | SB_PLL40_CORE #( 34 | .FEEDBACK_PATH("SIMPLE"), 35 | .DIVR(PLL_DIVR), 36 | .DIVF(PLL_DIVF), 37 | .DIVQ(PLL_DIVQ), 38 | .FILTER_RANGE(3'b001) 39 | ) 40 | pll ( 41 | .LOCK(), 42 | .RESETB(1'b1), 43 | .BYPASS(1'b0), 44 | .REFERENCECLK(clk_in), 45 | //.PLLOUTCORE(g_clk) 46 | .PLLOUTGLOBAL(g_clk) 47 | ); 48 | 49 | // Inputs and Outputs 50 | 51 | input wire clk_in; 52 | 53 | input wire reset_n_in; 54 | 55 | input wire sck0_in; 56 | input wire sdi0_in; 57 | input wire cs0_n_in; 58 | 59 | input wire sck1_in; 60 | input wire sdi1_in; 61 | output wire sdo1_out; 62 | input wire cs1_n_in; 63 | 64 | output wire ready_n_od_out; 65 | 66 | output wire status_led_n_out; 67 | 68 | top #( 69 | .POOL_SIZE(POOL_SIZE), 70 | .POOL_SIZE_LOG2(POOL_SIZE_LOG2), 71 | .BASE_TARGET(BASE_TARGET) 72 | ) 73 | u ( 74 | g_clk, 75 | reset_n_in, 76 | // Global data 77 | sck0_in, 78 | sdi0_in, 79 | cs0_n_in, 80 | // Daisy data 81 | sck1_in, 82 | sdi1_in, 83 | sdo1_out, 84 | cs1_n_in, 85 | // Success flags 86 | ready_n_od_out, 87 | // Indicators 88 | status_led_n_out 89 | ); 90 | 91 | endmodule 92 | -------------------------------------------------------------------------------- /src/top_up5k.v: -------------------------------------------------------------------------------- 1 | module top_up5k 2 | ( 3 | clk_in, 4 | reset_n_in, 5 | // Global data 6 | sck0_in, 7 | sdi0_in, 8 | cs0_n_in, 9 | // Daisy data 10 | sck1_in, 11 | sdi1_in, 12 | sdo1_out, 13 | cs1_n_in, 14 | // Success flags 15 | ready_n_od_out, 16 | // Indicators 17 | status_led_n_out 18 | ); 19 | 20 | `define SHAPOOL_NO_NONCE_OFFSET // Required for POOL_SIZE = 1 21 | 22 | parameter POOL_SIZE = 1; 23 | parameter POOL_SIZE_LOG2 = 0; 24 | parameter BASE_DIFFICULTY = 64; 25 | 26 | // 12 MHz ~ 56.25 MHz 27 | parameter PLL_DIVR = 4'b0000; 28 | parameter PLL_DIVF = 7'b1001010; 29 | parameter PLL_DIVQ = 3'b100; 30 | 31 | // Inputs and Outputs 32 | 33 | input wire clk_in; 34 | 35 | input wire reset_n_in; 36 | 37 | input wire sck0_in; 38 | input wire sdi0_in; 39 | input wire cs0_n_in; 40 | 41 | input wire sck1_in; 42 | input wire sdi1_in; 43 | output wire sdo1_out; 44 | input wire cs1_n_in; 45 | 46 | output wire ready_n_od_out; 47 | 48 | output wire status_led_n_out; 49 | 50 | top #( 51 | .POOL_SIZE(POOL_SIZE), 52 | .POOL_SIZE_LOG2(POOL_SIZE_LOG2), 53 | .BASE_DIFFICULTY(BASE_DIFFICULTY), 54 | .PLL_DIVR(PLL_DIVR), 55 | .PLL_DIVF(PLL_DIVF), 56 | .PLL_DIVQ(PLL_DIVQ) 57 | ) 58 | u ( 59 | clk_in, 60 | reset_n_in, 61 | // Global data 62 | sck0_in, 63 | sdi0_in, 64 | cs0_n_in, 65 | // Daisy data 66 | sck1_in, 67 | sdi1_in, 68 | sdo1_out, 69 | cs1_n_in, 70 | // Success flags 71 | ready_n_od_out, 72 | // Indicators 73 | status_led_n_out 74 | ); 75 | 76 | endmodule -------------------------------------------------------------------------------- /src/w_expand.v: -------------------------------------------------------------------------------- 1 | /* w_expand 2 | * 3 | * Asynchronously performs w_expand function given W[t] values. 4 | * 5 | * For SHA256, 6 | * 7 | * W(t) = SSIG1(W(t-2)) + W(t-7) + SSIG0(W(t-15)) + W(t-16) 8 | * 9 | */ 10 | module w_expand(w2, w7, w15, w16, out); 11 | 12 | input wire [31:0] w2; // W[t-2] 13 | input wire [31:0] w7; // W[t-7] 14 | input wire [31:0] w15; // W[t-15] 15 | input wire [31:0] w16; // W[t-16] 16 | output wire [31:0] out; 17 | 18 | wire [31:0] ssig0_w15; 19 | wire [31:0] ssig1_w2; 20 | 21 | // For SHA256, SSIG0(X) = ROTR7(X) XOR ROTR18(X) XOR SHR3(X) 22 | assign ssig0_w15 = { w15[ 6:0], w15[31: 7] } 23 | ^ { w15[17:0], w15[31:18] } 24 | ^ { 3'b0, w15[31: 3] }; 25 | 26 | // For SHA256, SSIG1(X) = ROTR17(X) XOR ROTR19(X) XOR SHR10(X) 27 | assign ssig1_w2 = { w2[16:0], w2[31:17] } 28 | ^ { w2[18:0], w2[31:19] } 29 | ^ { 10'b0, w2[31:10] }; 30 | 31 | assign out = ssig0_w15 + ssig1_w2 + w7 + w16; 32 | 33 | endmodule // w_expand 34 | --------------------------------------------------------------------------------