OpenCores
URL https://opencores.org/ocsvn/iota_pow_vhdl/iota_pow_vhdl/trunk

Subversion Repositories iota_pow_vhdl

[/] [iota_pow_vhdl/] [trunk/] [vhdl_cyclone10_lp/] [curl.vhd] - Rev 7

Compare with Previous | Blame | View Log

-- IOTA Pearl Diver VHDL Port
--
-- 2018 by Thomas Pototschnig <microengineer18@gmail.com,
-- http://microengineer.eu
-- discord: pmaxuw#8292
--
-- Permission is hereby granted, free of charge, to any person obtaining
-- a copy of this software and associated documentation files (the
-- "Software"), to deal in the Software without restriction, including
-- without limitation the rights to use, copy, modify, merge, publish,
-- distribute, sublicense, and/or sell copies of the Software, and to
-- permit persons to whom the Software is furnished to do so, subject to
-- the following conditions:
-- 
-- The above copyright notice and this permission notice shall be
-- included in all copies or substantial portions of the Software.
-- 
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWAR
 
library ieee;
 
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.index_table.all;
 
entity curl is
	generic
	(
		HASH_LENGTH : integer := 243;
		STATE_LENGTH : integer := 729; -- 3 * HASH_LENGTH;
		NONCE_LENGTH : integer := 81; -- HASH_LENGTH / 3;
		NUMBER_OF_ROUNDS : integer := 81;
		PARALLEL : integer := 7;
		INTERN_NONCE_LENGTH : integer	:= 32;
		BITS_MIN_WEIGHT_MAGINUTE_MAX : integer := 26;
		DATA_WIDTH : integer := 9;
		NONCE_OFFSET : integer := 162	-- we hope it nevery changes
	);
 
	port
	(
		clk : in std_logic;
		clk_slow : in std_logic;
		reset : in std_logic;
 
		spi_data_rx : in std_logic_vector(31 downto 0);
		spi_data_tx : out std_logic_vector(31 downto 0);
		spi_data_rxen : in std_logic;
		spi_data_strobe : out std_logic;
		overflow : out std_logic;
		running : out std_logic;
		found : out std_logic
	);
 
end curl;
 
architecture behv of curl is
 
subtype state_vector_type is std_logic_vector(PARALLEL-1 downto 0);
subtype mid_state_vector_type is std_logic_vector(DATA_WIDTH-1 downto 0);
subtype min_weight_magnitude_type is std_logic_vector(BITS_MIN_WEIGHT_MAGINUTE_MAX-1 downto 0);
 
type curl_state_array is array(integer range <>) of state_vector_type;
type mid_state_array is array(integer range <>) of mid_state_vector_type;
type min_weight_magnitude_array is array(integer range<>) of min_weight_magnitude_type;
 
signal curl_state_low : curl_state_array(STATE_LENGTH-1 downto 0);
signal curl_state_high : curl_state_array(STATE_LENGTH-1 downto 0);
 
signal data_low : mid_state_array((HASH_LENGTH/DATA_WIDTH)-1 downto 0);
signal data_high : mid_state_array((HASH_LENGTH/DATA_WIDTH)-1 downto 0);
 
 
signal curl_mid_state_low : std_logic_vector(STATE_LENGTH-1 downto 0);
signal curl_mid_state_high : std_logic_vector(STATE_LENGTH-1 downto 0);
 
signal flag_running : std_logic := '0';
signal flag_overflow : std_logic := '0';
signal flag_found : std_logic := '0';
signal flag_start : std_logic := '0';
 
signal flag_curl_finished : std_logic := '0';
 
type binary_nonce_array is array(integer range<>) of unsigned(INTERN_NONCE_LENGTH-1 downto 0);
 
 
signal binary_nonce : unsigned(INTERN_NONCE_LENGTH-1 downto 0);	
signal mask : state_vector_type;
signal min_weight_magnitude : min_weight_magnitude_type;
 
signal i_binary_nonce : unsigned(INTERN_NONCE_LENGTH-1 downto 0);	
signal tmp_weight_magnitude : min_weight_magnitude_array(0 to PARALLEL-1);
 
signal flag_curl_reset : std_logic;
signal flag_curl_write : std_logic;
signal flag_curl_do_curl : std_logic;
 
signal imask : state_vector_type;
 
 
 
function expand(b : std_logic) 
	return state_vector_type is
begin
	if b = '1' then
		return (others => '1');
	else
		return (others => '0');
	end if;
end expand;
 
 
begin
	overflow <= flag_overflow;
	running <= flag_running;
	found <= flag_found;
 
	process (clk_slow)
	-- because it looks prettier
		variable spi_cmd : std_logic_vector(5 downto 0);
		variable addrptr : unsigned(7 downto 0) := x"00";
	begin
		if rising_edge(clk_slow) then
			if reset='1' then
				min_weight_magnitude <= (others => '0');
				flag_start <= '0';
				spi_data_tx <= (others => '0');
				addrptr := x"00";
			else
				flag_start <= '0';
				flag_curl_reset <= '0';
				flag_curl_write <= '0';
				flag_curl_do_curl <= '0';
				spi_data_strobe <= '0';
 
-- new spi data received
				if spi_data_rxen = '1' then
					spi_cmd := spi_data_rx(31 downto 26);
					case spi_cmd is
						when "000000" => -- nop (mainly for reading back data)
						when "100000" => -- start / stop
							flag_start <= spi_data_rx(0);
							flag_curl_reset <= spi_data_rx(1);
							flag_curl_write <= spi_data_rx(2);
							flag_curl_do_curl <= spi_data_rx(3);
						when "010000" =>	-- write to wr address
							addrptr := (others => '0'); --unsigned(spi_data_rx(7 downto 0));
						when "001000" =>	-- write to data buffer
							if (addrptr <= (HASH_LENGTH/DATA_WIDTH)-1) then
								data_low(to_integer(addrptr)) <= spi_data_rx(DATA_WIDTH-1 downto 0);
								data_high(to_integer(addrptr)) <= spi_data_rx(DATA_WIDTH+8 downto DATA_WIDTH);
							end if;
							addrptr := addrptr + 1;
						when "000100" =>
							min_weight_magnitude <= spi_data_rx(BITS_MIN_WEIGHT_MAGINUTE_MAX-1 downto 0);
						when "000010" =>	-- read flags
							spi_data_tx(3 downto 0) <= flag_curl_finished & flag_overflow & flag_found & flag_running;
							spi_data_tx(7 downto 4) <= std_logic_vector(to_unsigned(PARALLEL, 4));
							spi_data_tx(8+(PARALLEL-1) downto 8) <= mask;
							spi_data_strobe <= '1';
						when "000001" => -- read nonce
							spi_data_tx(INTERN_NONCE_LENGTH-1 downto 0) <= std_logic_vector(binary_nonce);
							spi_data_strobe <= '1';
						when others =>
					end case; 
				end if;
			end if; 
		end if;
	end process;
 
	process (clk_slow)
		variable	state : integer range 0 to 7 := 0;
		variable round : integer range 0 to 127 := 0;
		variable tmp_index : integer range 0 to 1023;
 
		variable alpha : std_logic_vector(STATE_LENGTH-1 downto 0);
		variable beta : std_logic_vector(STATE_LENGTH-1 downto 0);
		variable gamma : std_logic_vector(STATE_LENGTH-1 downto 0);
		variable delta : std_logic_vector(STATE_LENGTH-1 downto 0);
 
	begin
		if rising_edge(clk_slow) then
			if reset='1' then
				state := 0;
			else
				case state is
					when 0 =>
						round := NUMBER_OF_ROUNDS;
						flag_curl_finished <= '1';
 
						if flag_curl_write = '1' then 
							for I in 0 to (HASH_LENGTH/DATA_WIDTH)-1 loop
								for J in 0 to DATA_WIDTH-1 loop
									tmp_index := I*DATA_WIDTH+J;
									curl_mid_state_low(tmp_index) <= data_low(I)(J);
									curl_mid_state_high(tmp_index) <= data_high(I)(J);
								end loop;
							end loop;
						elsif flag_curl_reset='1' then
							curl_mid_state_low <= (others => '1');
							curl_mid_state_high <= (others => '1');
						end if;
 
						if flag_curl_do_curl = '1' then
							round := NUMBER_OF_ROUNDS;
							flag_curl_finished <= '0';
							state := 1;
						end if;
					when 1 =>	-- do the curl hash round without any copying needed
						if round = 1 then
							state := 0;
						end if;
						for I in 0 to STATE_LENGTH-1 loop
							alpha(I) := curl_mid_state_low(index_table(I));
							beta(I) := curl_mid_state_high(index_table(I));
							gamma(I) := curl_mid_state_high(index_table(I+1));
 
							delta(I) := (alpha(I) or (not gamma(I))) and (curl_mid_state_low(index_table(I+1)) xor beta(I));
 
							curl_mid_state_low(I) <= not delta(I);
							curl_mid_state_high(I) <= (alpha(I) xor gamma(I)) or delta(I);
						end loop;
						round := round - 1;
					when others =>
						state := 0;
				end case;
			end if;
		end if;
	end process;
 
	process (clk)
		variable	state : integer range 0 to 63 := 0;
		variable round : integer range 0 to 127 := 0;
 
 
		variable i_min_weight_magnitude : min_weight_magnitude_type;
 
		-- temporary registers get optimized away
		variable alpha : curl_state_array(STATE_LENGTH-1 downto 0);
		variable beta : curl_state_array(STATE_LENGTH-1 downto 0);
		variable gamma : curl_state_array(STATE_LENGTH-1 downto 0);
		variable delta : curl_state_array(STATE_LENGTH-1 downto 0);
 
		variable tmp_highest_bit : integer range 0 to 31;
	begin
		if rising_edge(clk) then
			if reset='1' then
				state := 0;
				round := 0;
				flag_found <= '0';
				flag_running <= '0';
				flag_overflow <= '0';
				binary_nonce <= (others => '0');
				mask <= (others => '0');
--				curl_state_low <= (others => (others => '0'));
--				curl_state_high <= (others => (others => '0'));
--				tmp_weight_magnitude := (others => (others => '0'));
				i_binary_nonce <= (others => '0');
				imask <= (others => '0');
--				i_min_weight_magnitude := (others => '0');
--				alpha := (others => (others => '0'));
--				beta := (others => (others => '0'));
--				gamma := (others => (others => '0'));
--				delta := (others => (others => '0'));
--				tmp_index := 0;
				tmp_weight_magnitude <= (others => (others => '0'));
			else
				case state is
					when 0 =>
						flag_running <= '0';
					when others =>
						flag_running <= '1';
				end case;
 
				case state is
					when 0 =>
--						flag_running <= '0';
						if flag_start = '1' then
							i_binary_nonce <= x"00000000";
--							flag_running <= '1';
							state := 1;
						end if;
 
					-- do PoW
					when 1 =>	-- copy mid state and insert nonce
						i_min_weight_magnitude := min_weight_magnitude;
						flag_found <= '0';
						flag_overflow <= '0';
						binary_nonce <= i_binary_nonce;						
						-- pipelining
						i_binary_nonce <= i_binary_nonce + 1;
 
--						-- copy and fully expand mid-state to curl-state
						for I in 0 to STATE_LENGTH-1 loop
							if  I < NONCE_OFFSET or I > NONCE_OFFSET + NONCE_LENGTH - 1 then
								curl_state_low(I) <= expand(curl_mid_state_low(I));
								curl_state_high(I) <= expand(curl_mid_state_high(I));
							end if;
						end loop;
 
--						for I in 0 to NONCE_OFFSET-1 loop
--							curl_state_low(I) <= expand(curl_mid_state_low(I));
--							curl_state_high(I) <= expand(curl_mid_state_high(I));
--						end loop;
--
--						for I in NONCE_OFFSET + NONCE_LENGTH to STATE_LENGTH-1 loop
--							curl_state_low(I) <= expand(curl_mid_state_low(I));
--							curl_state_high(I) <= expand(curl_mid_state_high(I));
--						end loop;						
 
 
						-- fill all ... synthesizer is smart enough to optimize away what is not needed
						for I in NONCE_OFFSET to NONCE_OFFSET + NONCE_LENGTH - 1 loop
							curl_state_low(I) <= expand('1');
							curl_state_high(I) <= expand('1');
						end loop;
 
						-- calculate log2(x) by determining the place of highest set bit
						-- this is calculated on constants, so no logic needed
						tmp_highest_bit := 0;
						for I in 0 to 31 loop	-- 32 is enough ...^^
							if to_unsigned(PARALLEL-1, 32)(I) = '1' then
								tmp_highest_bit := I;
							end if;
						end loop;
 
--						-- generate bitmuster in first trit-arrays of nonce depending on PARALLEL setting
						-- this is calculated on constants, so no logic needed
						for I in 0 to PARALLEL-1 loop
							for J in 0 to tmp_highest_bit loop
								curl_state_low(NONCE_OFFSET+J)(I) <= to_unsigned(I, tmp_highest_bit+1)(J);
								curl_state_high(NONCE_OFFSET+J)(I) <= not to_unsigned(I, tmp_highest_bit+1)(J);
							end loop;
						end loop;
 
						-- insert and convert binary nonce to ternary nonce
						-- It's a fake ternary nonce but integer-values are strictly monotonously rising 
						-- with integer values of binary nonce.
						-- Doesn't bring the exact same result like reference implementation with real
						-- ternary adder - but it doesn't matter and it is way faster.
						-- conveniently put nonce counter at the end of nonce
						for I in 0 to INTERN_NONCE_LENGTH-1 loop
							curl_state_low(NONCE_OFFSET + NONCE_LENGTH - INTERN_NONCE_LENGTH + I) <= expand(i_binary_nonce(I));
							curl_state_high(NONCE_OFFSET + NONCE_LENGTH - INTERN_NONCE_LENGTH + I) <= not expand(i_binary_nonce(I));
						end loop;
 
						-- initialize round-counter
						round := NUMBER_OF_ROUNDS;
						if i_binary_nonce = x"ffffffff" then
							flag_overflow <= '1';
							state := 0;
						else
							state := 2;
						end if;
					when 2 =>	-- do the curl hash round without any copying needed
						if round = 1 then
							state := 3;
						end if;
						for I in 0 to STATE_LENGTH-1 loop
							alpha(I) := curl_state_low(index_table(I));
							beta(I) := curl_state_high(index_table(I));
							gamma(I) := curl_state_high(index_table(I+1));
 
							delta(I) := (alpha(I) or (not gamma(I))) and (curl_state_low(index_table(I+1)) xor beta(I));
 
							curl_state_low(I) <= not delta(I);
							curl_state_high(I) <= (alpha(I) xor gamma(I)) or delta(I);
						end loop;
						round := round - 1;
					when 3 =>  -- find out which solution - if any
 
						-- transform "vertical" trits to "horizontal" bits
						-- and compare with min weight magnitude mask
						for I in 0 to PARALLEL-1 loop
							for J in 0 to BITS_MIN_WEIGHT_MAGINUTE_MAX-1 loop
								tmp_weight_magnitude(I)(J) <= curl_state_low(HASH_LENGTH - 1 - J)(I) and curl_state_high(HASH_LENGTH - 1 - J)(I) and i_min_weight_magnitude(J);
							end loop;
						end loop;
 
						-- pipelining
						imask <= (others => '0');
						for I in 0 to PARALLEL-1 loop
							if tmp_weight_magnitude(I) = i_min_weight_magnitude then
								imask(I) <= '1';
							end if;
						end loop;
 
						-- pipelining
						if unsigned(imask) = 0 then
							state :=1;
						else
							flag_found <= '1';
							mask <= imask;
							state :=0;
						end if;
					when others =>
						state := 0;
				end case;
			end if;
		end if;
	end process;
end behv;
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.